├── test ├── .npmignore └── spec.js ├── .gitignore ├── .travis.yml ├── package.json ├── README.md └── main.js /test/.npmignore: -------------------------------------------------------------------------------- 1 | .yml 2 | .git -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode/launch.json 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - node 5 | - lts/* 6 | branches: 7 | only: 8 | - master 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "trello", 3 | "version": "0.13.0", 4 | "author": "Norbert Eder ", 5 | "description": "This module provides an easy way to make requests to the Trello API.", 6 | "contributors": [ 7 | { 8 | "name": "Graeme Foster", 9 | "email": "graemef@gmail.com" 10 | }, 11 | { 12 | "name": "Mike Beasley", 13 | "email": "mbeasley@emcien.com" 14 | }, 15 | { 16 | "name": "Dario Kondratiuk", 17 | "email": "dariokondratiuk@gmail.com" 18 | }, 19 | { 20 | "name": "Sean Madden", 21 | "email": "sdm350@gmail.com" 22 | }, 23 | { 24 | "name": "Siôn Le Roux", 25 | "email": "sinisterstuf@gmail.com" 26 | }, 27 | { 28 | "name": "Mike Garuccio", 29 | "url": "https://github.com/mgaruccio" 30 | }, 31 | { 32 | "name": "Karel Piwko", 33 | "url": "https://github.com/kpiwko" 34 | }, 35 | { 36 | "name": "Aaron Sherrill", 37 | "url": "https://github.com/motionharvest" 38 | } 39 | ], 40 | "scripts": { 41 | "test": "node node_modules/mocha/bin/mocha" 42 | }, 43 | "main": "main.js", 44 | "homepage": "https://github.com/norberteder/trello", 45 | "repository": { 46 | "type": "git", 47 | "url": "https://github.com/norberteder/trello.git" 48 | }, 49 | "keywords": [ 50 | "trello" 51 | ], 52 | "dependencies": { 53 | "es6-promise": "^4.2.8", 54 | "object-assign": "~4.1.0", 55 | "needle": "3.0.0" 56 | }, 57 | "devDependencies": { 58 | "mocha": "^9.1.3", 59 | "chai": "^4.3.4", 60 | "sinon": "^12.0.1", 61 | "sinon-chai": "^3.7.0", 62 | "q": "1.5.1" 63 | }, 64 | "license": "MIT", 65 | "engines": { 66 | "node": ">= 0.10.x" 67 | }, 68 | "optionalDependencies": {} 69 | } 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/norberteder/trello.svg?branch=master)](https://travis-ci.org/norberteder/trello) 2 | 3 | # trello 4 | ## A simple asynchronous client for [Trello](http://www.trello.com) 5 | 6 | This is a wrapper for some of the Trello HTTP API. Please feel free to add any other pieces you need! :) 7 | 8 | ## Installation 9 | npm install trello 10 | 11 | ## Usage 12 | Log in to Trello and visit [trello.com/app-key](https://trello.com/app-key) to get a `token` and `app key`. These need to be supplied when you create the Trello object (see below). 13 | 14 | ## Example 15 | ```javascript 16 | var Trello = require("trello"); 17 | var trello = new Trello("MY APPLICATION KEY", "MY USER TOKEN"); 18 | 19 | trello.addCard('Clean car', 'Wax on, wax off', myListId, 20 | function (error, trelloCard) { 21 | if (error) { 22 | console.log('Could not add card:', error); 23 | } 24 | else { 25 | console.log('Added card:', trelloCard); 26 | } 27 | }); 28 | ``` 29 | 30 | ## Callback or promise 31 | API calls can either execute a callback or return a promise. To return a promise just omit the callback parameter. 32 | 33 | ```javascript 34 | //Callback 35 | trello.getCardsOnList(listId, callback); 36 | 37 | //Promise 38 | var cardsPromise = trello.getCardsOnList(listId); 39 | cardsPromise.then((cards) => { 40 | //do stuff 41 | }) 42 | ``` 43 | 44 | ## Requests to API endpoints, not supported by this lib yet 45 | 46 | ```javascript 47 | // Get all registered tokens and webhooks 48 | // Url will look like: https://api.trello.com/1/members/me/tokens?webhooks=true&key=YOURKEY&token=YOURTOKEN 49 | trello.makeRequest('get', '/1/members/me/tokens', { webhooks: true }) 50 | .then((res) => { 51 | console.log(res) 52 | }); 53 | ``` 54 | 55 | ## Available functions 56 | 57 | ### Add 58 | 59 | * addAttachmentToCard 60 | * addBoard 61 | * addCard 62 | * addCardWithExtraParams 63 | * addChecklistToCard 64 | * addCommentToCard 65 | * addCustomField 66 | * addDueDateToCard 67 | * addExistingChecklistToCard 68 | * addItemToChecklist 69 | * addLabelOnBoard 70 | * addLabelToCard 71 | * addListToBoard 72 | * addMemberToBoard 73 | * addMemberToCard 74 | * addOptionToCustomField 75 | * addStickerToCard 76 | * addWebhook 77 | * copyBoard 78 | * setCustomFieldOnCard 79 | 80 | ### Delete 81 | 82 | * deleteCard 83 | * deleteLabel 84 | * deleteLabelFromCard 85 | * delMemberFromCard 86 | * deleteWebhook 87 | 88 | ### Get 89 | 90 | * getActionsOnBoard 91 | * getBoardMembers 92 | * getBoards 93 | * getCard 94 | * getCardsForList 95 | * getCardsOnBoard 96 | * getCardsOnBoardWithExtraParams 97 | * getCardsOnList 98 | * getCardsOnListWithExtraParams 99 | * getCardStickers 100 | * getChecklistsOnCard 101 | * getCustomFieldsOnBoard 102 | * getLabelsForBoard 103 | * getListsOnBoard 104 | * getListsOnBoardByFilter 105 | * getMember 106 | * getMemberCards 107 | * getOrganization 108 | * getOrgBoards 109 | * getOrgMembers 110 | 111 | ### Update 112 | 113 | * renameList 114 | * updateBoardPref 115 | * updateCard 116 | * updateCardDescription 117 | * updateCardList 118 | * updateCardName 119 | * updateCardPos 120 | * updateChecklist 121 | * updateLabel 122 | * updateLabelColor 123 | * updateLabelName 124 | 125 | Everything that is not available as a function can be requested by calling `makeRequest`. 126 | 127 | ## History 128 | 129 | ### 0.13.0 130 | 131 | * Add function `getOrganization` 132 | 133 | ### 0.12.0 134 | 135 | * Replaced `restler` with `needle` 136 | 137 | ### 0.11.0 138 | 139 | * Update optional fields 140 | * Add optional field queries 141 | * Add function `addCustomField` 142 | * Add function `addOptionToCustomField` 143 | * Add function `setCustomFieldOnCard` 144 | * Add function `updateCardPos` 145 | * Add function `delMemberFromCard` 146 | 147 | ### 0.10.0 148 | 149 | * Add `copyBoard` functionality 150 | * Add `getCustomFieldsOnBoard` 151 | * Add `getActionsOnBoard` 152 | 153 | ### 0.9.1 154 | 155 | * Added trailing slash to /boards/ call 156 | 157 | ### 0.9.0 158 | 159 | * New function `getCardsOnBoardWithExtraParams` 160 | * New function `getCardsOnListWithExtraParams` 161 | * New function `addDueDateToCard` 162 | 163 | ### 0.8.0 164 | 165 | * Rename list fixed 166 | * Handle API rate limit by retries 167 | * New function `addCardWithExtraParams` 168 | 169 | ### 0.7.0 170 | 171 | * Public visibility for `makeRequest` 172 | 173 | ### 0.6.0 174 | 175 | * added `getMember` 176 | * added `getCardStickers` 177 | * added `addStickerToCard` 178 | * added `getOrgBoards` 179 | * added `getMemberCards` 180 | * added `updateBoardPref` 181 | * added `addMemberToBoard` 182 | 183 | ### 0.5.1 184 | 185 | * added `renameList` 186 | * added `addChecklistToCard` 187 | * added `getChecklistsOnCard` 188 | * added `addExistingChecklistToCard` 189 | * added `updateChecklist` 190 | * added `getOrgMembers` 191 | * API methods now return the promise 192 | 193 | ### 0.5.0 194 | 195 | * Support of promises 196 | * Basic support of Labeling: 197 | * getLabelsForBoard 198 | * addLabelOnBoard 199 | * deleteLabel 200 | * addLabelToCard 201 | * deleteLabelFromCard 202 | 203 | ### 0.4.1 204 | 205 | * Updated dev dependencies 206 | 207 | ### 0.4.0 208 | 209 | * One-time listener 210 | * `addAttachmentToCard` added 211 | * Updated `restler` dependency 212 | * Node.js support >= 0.10.x / removed 0.6 and 0.8 213 | 214 | ### 0.3.0 215 | 216 | * Project `trello_ex` merged again with original project `trello` 217 | * Using 'restler' again 218 | 219 | ### 0.2.0 220 | 221 | * `getBoards` added 222 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | require('es6-promise').polyfill(); 2 | var rest = require('needle'); 3 | var objectAssign = require('object-assign'); 4 | 5 | var minRequestDelay = 500; 6 | var maxRequestDelay = 7000; 7 | 8 | var Trello = function (key, token) { 9 | this.uri = "https://api.trello.com"; 10 | this.key = key; 11 | this.token = token; 12 | }; 13 | 14 | Trello.prototype.createQuery = function () { 15 | return {key: this.key, token: this.token}; 16 | }; 17 | 18 | function makeRequest(fn, uri, options, callback) { 19 | if (callback) { 20 | var completeCallback = function (result, response) { 21 | // in case we hit HTTP 429, delay requests by random timeout in between minRequestDelay and maxRequestDelay 22 | // http://help.trello.com/article/838-api-rate-limits 23 | if(response && response.statusCode === 429) { 24 | setTimeout(() => { 25 | fn(uri, options).once('complete', completeCallback) 26 | }, Math.floor(Math.random() * (maxRequestDelay - minRequestDelay)) + minRequestDelay); 27 | } 28 | else if (result instanceof Error) { 29 | callback(result, null); 30 | } else if (response != null && response.statusCode >= 400) { 31 | const rv = new Error(result) 32 | rv.response = response 33 | callback(rv, null) 34 | } else { 35 | callback(null, result); 36 | } 37 | } 38 | 39 | fn(uri, options).once('complete', completeCallback); 40 | 41 | } else { 42 | return new Promise((resolve, reject) => { 43 | 44 | var completeCallback = function (result, response) { 45 | // in case we hit HTTP 429, delay requests by random timeout in between minRequestDelay and maxRequestDelay 46 | // http://help.trello.com/article/838-api-rate-limits 47 | if(response && response.statusCode === 429) { 48 | setTimeout(() => { 49 | fn(uri, options).once('complete', completeCallback) 50 | }, Math.floor(Math.random() * (maxRequestDelay - minRequestDelay)) + minRequestDelay); 51 | } 52 | else if (result instanceof Error) { 53 | reject(result); 54 | } else if (response != null && response.statusCode >= 400) { 55 | const rv = new Error(result) 56 | rv.response = response 57 | reject(rv) 58 | } else { 59 | resolve(result); 60 | } 61 | } 62 | 63 | fn(uri, options).once('complete', completeCallback); 64 | }); 65 | } 66 | } 67 | 68 | Trello.prototype.makeRequest = function (requestMethod, path, options, callback) { 69 | options = options || {}; 70 | 71 | if (typeof requestMethod !== 'string') { 72 | throw new TypeError("requestMethod should be a string"); 73 | } 74 | if (typeof options !== 'object') { 75 | throw new TypeError("options should be an object"); 76 | } 77 | 78 | var method = requestMethod.toLowerCase(); 79 | var methods = { 80 | 'post': rest.post, 81 | 'postjson': rest.post, 82 | 'get': rest.get, 83 | 'put': rest.put, 84 | 'putjson' : rest.put, 85 | 'delete': rest.delete 86 | }; 87 | 88 | if (!methods[method]) { 89 | throw new Error("Unsupported requestMethod. Pass one of these methods: POST, GET, PUT, DELETE."); 90 | } 91 | 92 | var keyTokenObj = this.createQuery(); 93 | var query = objectAssign({}, options, keyTokenObj); 94 | 95 | if(method.indexOf('json') !== -1) { 96 | var jsonOptions = {}; 97 | jsonOptions.headers = {'Content-Type' : 'application/json'} 98 | jsonOptions.data = options.data; 99 | jsonOptions.query = query; 100 | delete options.data; 101 | return makeRequest(methods[method], this.uri + path, jsonOptions, callback) 102 | } else { 103 | return makeRequest(methods[method], this.uri + path, {query: query}, callback) 104 | } 105 | }; 106 | Trello.prototype.addBoard = function (name, description, organizationId, callback) { 107 | var query = this.createQuery(); 108 | query.name = name; 109 | 110 | if (description !== null) 111 | query.desc = description; 112 | if (organizationId !== null) 113 | query.idOrganization = organizationId; 114 | 115 | return makeRequest(rest.post, this.uri + '/1/boards/', {query: query}, callback); 116 | }; 117 | 118 | Trello.prototype.copyBoard = function (name, sourceBoardId, callback) { 119 | var query = this.createQuery(); 120 | query.name = name; 121 | query.idBoardSource = sourceBoardId; 122 | 123 | return makeRequest(rest.post, this.uri + '/1/boards/', {query: query}, callback); 124 | }; 125 | 126 | Trello.prototype.updateBoardPref = function (boardId, field, value, callback) { 127 | var query = this.createQuery(); 128 | query.value = value; 129 | 130 | return makeRequest(rest.put, this.uri + '/1/boards/' + boardId + '/prefs/' + field, {query: query}, callback); 131 | }; 132 | 133 | Trello.prototype.addCard = function (name, description, listId, callback) { 134 | var query = this.createQuery(); 135 | query.name = name; 136 | query.idList = listId; 137 | 138 | if (description !== null) 139 | query.desc = description; 140 | 141 | return makeRequest(rest.post, this.uri + '/1/cards', {query: query}, callback); 142 | }; 143 | 144 | Trello.prototype.addCardWithExtraParams = function(name, extraParams, listId, callback) { 145 | var query = this.createQuery(); 146 | query.name = name; 147 | query.idList = listId; 148 | 149 | Object.assign(query, extraParams); 150 | 151 | return makeRequest(rest.post, this.uri + '/1/cards', {query: query}, callback); 152 | }; 153 | 154 | Trello.prototype.getCard = function (boardId, cardId, callback) { 155 | if(boardId === null) 156 | return makeRequest(rest.get, this.uri + '/1/cards/' + cardId, {query: this.createQuery()}, callback); 157 | return makeRequest(rest.get, this.uri + '/1/boards/' + boardId + '/cards/' + cardId, {query: this.createQuery()}, callback); 158 | }; 159 | 160 | Trello.prototype.getCardById = function (cardId, callback) { 161 | return makeRequest(rest.get, this.uri + '/1/cards/' + cardId, {query: this.createQuery()}, callback); 162 | }; 163 | 164 | Trello.prototype.getCardsForList = function(listId, actions, callback) { 165 | var query = this.createQuery(); 166 | if (actions) 167 | query.actions = actions; 168 | return makeRequest(rest.get, this.uri + '/1/lists/' + listId + '/cards', {query: query}, callback); 169 | }; 170 | 171 | Trello.prototype.renameList = function (listId, name, callback) { 172 | var query = this.createQuery(); 173 | query.value = name; 174 | 175 | return makeRequest(rest.put, this.uri + '/1/lists/' + listId + '/name', {query: query}, callback); 176 | }; 177 | 178 | Trello.prototype.addListToBoard = function (boardId, name, callback) { 179 | var query = this.createQuery(); 180 | query.name = name; 181 | 182 | return makeRequest(rest.post, this.uri + '/1/boards/' + boardId + '/lists', {query: query}, callback); 183 | }; 184 | 185 | Trello.prototype.addMemberToBoard = function (boardId, memberId, type, callback) { 186 | var query = this.createQuery(); 187 | var data = {type: type}; // Valid Values: 'normal','admin','observer' 188 | 189 | return makeRequest(rest.put, this.uri + '/1/boards/' + boardId + '/members/' + memberId, { data: data, query: query }, callback); 190 | }; 191 | 192 | Trello.prototype.addCommentToCard = function (cardId, comment, callback) { 193 | var query = this.createQuery(); 194 | query.text = comment; 195 | 196 | return makeRequest(rest.post, this.uri + '/1/cards/' + cardId + '/actions/comments', {query: query}, callback); 197 | }; 198 | 199 | Trello.prototype.addAttachmentToCard = function (cardId, url, callback) { 200 | var query = this.createQuery(); 201 | query.url = url; 202 | 203 | return makeRequest(rest.post, this.uri + '/1/cards/' + cardId + '/attachments', {query: query}, callback); 204 | }; 205 | 206 | Trello.prototype.addMemberToCard = function (cardId, memberId, callback) { 207 | var query = this.createQuery(); 208 | query.value = memberId; 209 | 210 | return makeRequest(rest.post, this.uri + '/1/cards/' + cardId + '/members', {query: query}, callback); 211 | }; 212 | Trello.prototype.delMemberFromCard = function (cardId, memberId, callback) { 213 | var query = this.createQuery(); 214 | 215 | return makeRequest(rest.delete, this.uri + '/1/cards/' + cardId + '/members/' + memberId, {query: query}, callback); 216 | }; 217 | 218 | Trello.prototype.getBoards = function(memberId, callback) { 219 | return makeRequest(rest.get, this.uri + '/1/members/' + memberId + '/boards', {query: this.createQuery()}, callback); 220 | }; 221 | 222 | Trello.prototype.getOrganization = function (organizationId, callback) { 223 | return makeRequest(rest.get, this.uri + '/1/organizations/' + organizationId, {query: this.createQuery()}, callback); 224 | }; 225 | 226 | Trello.prototype.getOrgBoards = function (organizationId, callback) { 227 | return makeRequest(rest.get, this.uri + '/1/organizations/' + organizationId + '/boards', {query: this.createQuery()}, callback); 228 | }; 229 | 230 | Trello.prototype.addChecklistToCard = function (cardId, name, callback) { 231 | var query = this.createQuery(); 232 | query.name = name; 233 | 234 | return makeRequest(rest.post, this.uri + '/1/cards/' + cardId + '/checklists', { query: query }, callback); 235 | }; 236 | 237 | Trello.prototype.addExistingChecklistToCard = function (cardId, checklistId, callback) { 238 | var query = this.createQuery(); 239 | query.idChecklistSource = checklistId; 240 | 241 | return makeRequest(rest.post, this.uri + '/1/cards/' + cardId + '/checklists', { query: query }, callback); 242 | }; 243 | 244 | Trello.prototype.getChecklistsOnCard = function (cardId, callback) { 245 | return makeRequest(rest.get, this.uri + '/1/cards/' + cardId + '/checklists', {query: this.createQuery()}, callback); 246 | }; 247 | 248 | Trello.prototype.getActionsOnCard = function (cardId, callback) { 249 | return makeRequest(rest.get, this.uri + '/1/cards/' + cardId + '/actions', {query: this.createQuery()}, callback); 250 | }; 251 | 252 | Trello.prototype.addItemToChecklist = function (checkListId, name, pos, callback) { 253 | var query = this.createQuery(); 254 | query.name = name; 255 | query.pos = pos; 256 | 257 | return makeRequest(rest.post, this.uri + '/1/checklists/' + checkListId + '/checkitems', {query: query}, callback); 258 | }; 259 | 260 | Trello.prototype.updateCard = function (cardId, field, value, callback) { 261 | var query = this.createQuery(); 262 | query.value = value; 263 | 264 | return makeRequest(rest.put, this.uri + '/1/cards/' + cardId + '/' + field, {query: query}, callback); 265 | }; 266 | 267 | Trello.prototype.updateChecklist = function (checklistId, field, value, callback) { 268 | var query = this.createQuery(); 269 | query.value = value; 270 | 271 | return makeRequest(rest.put, this.uri + '/1/checklists/' + checklistId + '/' + field, {query: query}, callback); 272 | }; 273 | 274 | Trello.prototype.updateCardName = function (cardId, name, callback) { 275 | return this.updateCard(cardId, 'name', name, callback); 276 | }; 277 | 278 | Trello.prototype.updateCardDescription = function (cardId, description, callback) { 279 | return this.updateCard(cardId, 'desc', description, callback); 280 | }; 281 | 282 | Trello.prototype.updateCardList = function (cardId, listId, callback) { 283 | return this.updateCard(cardId, 'idList', listId, callback); 284 | }; 285 | 286 | Trello.prototype.getMember = function(memberId, callback) { 287 | return makeRequest(rest.get, this.uri + '/1/member/' + memberId, {query: this.createQuery()}, callback); 288 | }; 289 | 290 | Trello.prototype.getMemberCards = function (memberId, callback) { 291 | return makeRequest(rest.get, this.uri + '/1/members/' + memberId + '/cards', {query: this.createQuery()}, callback); 292 | }; 293 | 294 | Trello.prototype.getBoardMembers = function (boardId, callback) { 295 | return makeRequest(rest.get, this.uri + '/1/boards/' + boardId + '/members', {query: this.createQuery()}, callback); 296 | }; 297 | 298 | Trello.prototype.getOrgMembers = function (organizationId, callback) { 299 | return makeRequest(rest.get, this.uri + '/1/organizations/' + organizationId + '/members', {query: this.createQuery()}, callback); 300 | }; 301 | 302 | Trello.prototype.getListsOnBoard = function (boardId, fields = 'all', callback,) { 303 | var query = this.createQuery(); 304 | query.fields = fields; 305 | return makeRequest(rest.get, this.uri + '/1/boards/' + boardId + '/lists', {query: query}, callback); 306 | }; 307 | 308 | Trello.prototype.getListsOnBoardByFilter = function(boardId, filter, callback) { 309 | var query = this.createQuery(); 310 | query.filter = filter; 311 | return makeRequest(rest.get, this.uri + '/1/boards/' + boardId + '/lists', {query: query}, callback); 312 | }; 313 | 314 | Trello.prototype.getCardsOnBoard = function (boardId, callback) { 315 | return makeRequest(rest.get, this.uri + '/1/boards/' + boardId + '/cards', {query: this.createQuery()}, callback); 316 | }; 317 | 318 | Trello.prototype.getCardsOnBoardWithExtraParams = function (boardId, extraParams, fields = 'all', callback) { 319 | var query = this.createQuery(); 320 | Object.assign(query, extraParams); 321 | query.fields = fields 322 | 323 | return makeRequest(rest.get, this.uri + '/1/boards/' + boardId + '/cards', {query: query}, callback); 324 | } 325 | 326 | Trello.prototype.getCustomFieldsOnBoard = function (boardId, callback) { 327 | return makeRequest(rest.get, this.uri + '/1/boards/' + boardId + '/customFields', {query: this.createQuery()}, callback); 328 | }; 329 | 330 | Trello.prototype.addCustomField = function (boardId, name, callback) { 331 | var query = this.createQuery(); 332 | var data = { 333 | idModel: boardId, 334 | modelType: "board", 335 | name: name, 336 | options: [], 337 | pos: "bottom", 338 | type: "list" 339 | }; 340 | return makeRequest(rest.post, this.uri + '/1/customFields', {data: data, query:query}, callback); 341 | }; 342 | 343 | Trello.prototype.addOptionToCustomField = function (customField, value, callback) { 344 | var query = this.createQuery(); 345 | var data = { 346 | pos: "bottom", 347 | value: { 348 | text: value 349 | } 350 | }; 351 | return makeRequest(rest.post, this.uri + '/1/customFields/' + customField + '/options', {data: data, query:query}, callback); 352 | }; 353 | 354 | Trello.prototype.setCustomFieldOnCard = function (cardId, customField, value, callback) { 355 | var query = this.createQuery(); 356 | 357 | return makeRequest(rest.put, this.uri + '/1/card/' + cardId + '/customField/' + customField + '/item', {data: value, query: query}, callback); 358 | }; 359 | 360 | Trello.prototype.getCardsOnList = function (listId, callback) { 361 | return makeRequest(rest.get, this.uri + '/1/lists/' + listId + '/cards', {query: this.createQuery()}, callback); 362 | }; 363 | 364 | Trello.prototype.getCardsOnListWithExtraParams = function (listId, extraParams, callback) { 365 | var query = this.createQuery(); 366 | Object.assign(query, extraParams); 367 | 368 | return makeRequest(rest.get, this.uri + '/1/lists/' + listId + '/cards', {query: query}, callback); 369 | } 370 | 371 | Trello.prototype.deleteCard = function (cardId, callback) { 372 | return makeRequest(rest.delete, this.uri + '/1/cards/' + cardId, {query: this.createQuery()}, callback); 373 | }; 374 | 375 | Trello.prototype.addWebhook = function (description, callbackUrl, idModel, callback) { 376 | var query = this.createQuery(); 377 | var data = {}; 378 | 379 | data.description = description; 380 | data.callbackURL = callbackUrl; 381 | data.idModel = idModel; 382 | 383 | return makeRequest(rest.post, this.uri + '/1/tokens/' + this.token + '/webhooks/', { data: data, query: query }, callback); 384 | }; 385 | 386 | Trello.prototype.deleteWebhook = function (webHookId, callback) { 387 | var query = this.createQuery(); 388 | 389 | return makeRequest(rest.delete, this.uri + '/1/webhooks/' + webHookId, { query: query }, callback); 390 | }; 391 | 392 | Trello.prototype.getLabelsForBoard = function(boardId, callback) { 393 | return makeRequest(rest.get, this.uri + '/1/boards/' + boardId + '/labels', {query:this.createQuery()}, callback); 394 | }; 395 | 396 | 397 | Trello.prototype.getActionsOnBoard = function(boardId, callback) { 398 | return makeRequest(rest.get, this.uri + '/1/boards/' + boardId + '/actions', {query:this.createQuery()}, callback); 399 | }; 400 | 401 | Trello.prototype.getCustomFieldsOnBoard = function(boardId, callback) { 402 | return makeRequest(rest.get, this.uri + '/1/boards/' + boardId + '/customFields', {query:this.createQuery()}, callback); 403 | }; 404 | 405 | Trello.prototype.addLabelOnBoard = function(boardId, name, color, callback) { 406 | var query = this.createQuery(); 407 | var data = { 408 | idBoard: boardId, 409 | color: color, 410 | name: name 411 | }; 412 | 413 | return makeRequest(rest.post, this.uri + '/1/labels', {data: data, query:query}, callback); 414 | }; 415 | 416 | Trello.prototype.deleteLabel = function(labelId, callback) { 417 | return makeRequest(rest.delete, this.uri + '/1/labels/' + labelId, {query: this.createQuery()}, callback); 418 | }; 419 | 420 | Trello.prototype.addLabelToCard = function(cardId, labelId, callback) { 421 | var query = this.createQuery(); 422 | var data = { value: labelId }; 423 | return makeRequest(rest.post, this.uri+'/1/cards/' + cardId + '/idLabels', {query:query, data:data}, callback); 424 | }; 425 | 426 | Trello.prototype.deleteLabelFromCard = function(cardId, labelId, callback){ 427 | return makeRequest(rest.delete, this.uri + '/1/cards/' + cardId + '/idLabels/'+labelId, {query: this.createQuery()}, callback); 428 | }; 429 | 430 | Trello.prototype.updateCardPos = function(cardId, position, callback) { 431 | var query = this.createQuery(); 432 | var data = { pos: position }; 433 | 434 | return makeRequest(rest.put, this.uri + '/1/cards/' + cardId, {query: query, data: data}, callback); 435 | }; 436 | 437 | Trello.prototype.updateLabel = function (labelId, field, value, callback) { 438 | var query = this.createQuery(); 439 | query.value = value; 440 | 441 | return makeRequest(rest.put, this.uri + '/1/labels/' + labelId + '/' + field, {query: query}, callback); 442 | }; 443 | 444 | Trello.prototype.updateLabelName = function (labelId, name, callback) { 445 | return this.updateLabel(labelId, 'name', name, callback); 446 | }; 447 | 448 | Trello.prototype.updateLabelColor = function (labelId, color, callback) { 449 | return this.updateLabel(labelId, 'color', color, callback); 450 | }; 451 | 452 | Trello.prototype.getCardStickers = function (cardId, callback) { 453 | return makeRequest(rest.get, this.uri + '/1/cards/' + cardId + '/stickers', {query: this.createQuery()}, callback); 454 | }; 455 | 456 | Trello.prototype.addStickerToCard = function(cardId, image, left, top, zIndex, rotate, callback) { 457 | var query = this.createQuery(); 458 | var data = { 459 | image: image, 460 | top: top, 461 | left: left, 462 | zIndex: zIndex, 463 | rotate: rotate, 464 | }; 465 | return makeRequest(rest.post, this.uri+'/1/cards/' + cardId + '/stickers', {query:query, data:data}, callback); 466 | }; 467 | 468 | Trello.prototype.addDueDateToCard = function (cardId, dateValue, callback) { 469 | var query = this.createQuery(); 470 | query.value = dateValue; 471 | 472 | return makeRequest(rest.put, this.uri + '/1/cards/' + cardId + '/due', {query: query}, callback); 473 | }; 474 | 475 | Trello.prototype.updateCustomFieldOnCard = function (cardId, field, value, callback) { 476 | var options = { 477 | query : this.createQuery(), 478 | headers : {'Content-Type' : 'application/json'}, 479 | data : { value : value} 480 | }; 481 | return makeRequest(rest.put, this.uri + '/1/cards/' + cardId + '/customField/' + field + '/item', options, callback); 482 | }; 483 | 484 | Trello.prototype.getCustomFieldsOnCard = function (cardId, callback) { 485 | return makeRequest(rest.get, this.uri + '/1/cards/' + cardId + '/customFieldItems', {query: this.createQuery()}, callback); 486 | }; 487 | 488 | Trello.prototype.getAttachmentsOnCard = function (cardId, callback) { 489 | return makeRequest(rest.get, this.uri + '/1/cards/' + cardId + '/attachments', {query: this.createQuery()}, callback); 490 | }; 491 | 492 | 493 | module.exports = Trello; 494 | -------------------------------------------------------------------------------- /test/spec.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var sinon = require('sinon'); 3 | var sinonChai = require("sinon-chai"); 4 | var q = require('q'); 5 | var fs = require('fs'); 6 | 7 | chai.should(); 8 | chai.use(sinonChai); 9 | 10 | var restler = require('needle'); 11 | var Trello = require('../main'); 12 | 13 | describe('Trello', function () { 14 | var trello; 15 | 16 | beforeEach(function () { 17 | trello = new Trello('key', 'token'); 18 | }); 19 | 20 | describe('makeRequest', function () { 21 | var expect = chai.expect; 22 | 23 | it('should throw error if type of options passed is not object', function () { 24 | expect(trello.makeRequest.bind(trello, 'GET', 'somePath', 'wrongOptions')).to.throw(TypeError) 25 | }); 26 | 27 | it('should throw error if type of a method passed is not string', function () { 28 | expect(trello.makeRequest.bind(trello, restler.post, 'somePath', {})).to.throw(TypeError) 29 | }); 30 | 31 | it('should throw error if a method passed is not one of these: POST, GET, PUT, DELETE', function () { 32 | expect(trello.makeRequest.bind(trello, 'patch', 'somePath', {})).to.throw(Error) 33 | }); 34 | 35 | it('should not throw error if no options are passed', function () { 36 | expect(trello.makeRequest.bind(trello, 'GET', '/1/members/me/tokens')).to.not.throw(Error); 37 | }); 38 | 39 | it('should not throw error if a method passed is POST', function (done) { 40 | sinon.stub(restler, 'post').callsFake(function (path, options) { 41 | return {once: function (event, callback) { 42 | callback(null, null); 43 | }}; 44 | }); 45 | 46 | expect(trello.makeRequest.bind(trello, 'POST', 'somePath', {}, function () {})).to.not.throw(Error); 47 | restler.post.restore(); 48 | done(); 49 | }); 50 | 51 | it('should not throw error if a method passed is GET', function (done) { 52 | sinon.stub(restler, 'get').callsFake(function (path, options) { 53 | return {once: function (event, callback) { 54 | callback(null, null); 55 | }}; 56 | }); 57 | 58 | expect(trello.makeRequest.bind(trello, 'GET', 'somePath', {}, function () {})).to.not.throw(Error); 59 | restler.get.restore(); 60 | done(); 61 | }); 62 | 63 | it('should not throw error if a method passed is PUT', function (done) { 64 | sinon.stub(restler, 'put').callsFake(function (path, options) { 65 | return {once: function (event, callback) { 66 | callback(null, null); 67 | }}; 68 | }); 69 | 70 | expect(trello.makeRequest.bind(trello, 'PUT', 'somePath', {}, function () {})).to.not.throw(Error); 71 | restler.put.restore(); 72 | done(); 73 | }); 74 | 75 | it('should not throw error if a method passed is DELETE', function (done) { 76 | sinon.stub(restler, 'delete').callsFake(function (path, options) { 77 | return {once: function (event, callback) { 78 | callback(null, null); 79 | }}; 80 | }); 81 | 82 | expect(trello.makeRequest.bind(trello, 'DELETE', 'somePath', {}, function () {})).to.not.throw(Error); 83 | restler.delete.restore(); 84 | done(); 85 | }); 86 | 87 | it('should not mutate passed options object', function (done) { 88 | sinon.stub(restler, 'get').callsFake(function (path, options) { 89 | return {once: function (event, callback) { 90 | callback(null, null); 91 | }}; 92 | }); 93 | var options = { webhooks: true }; 94 | trello.makeRequest("get", "/1/cards", options) 95 | .then(function (result) { 96 | expect(Object.keys(options).length, 1, "options object was mutated"); 97 | expect(options.webhooks, true) 98 | }); 99 | restler.get.restore(); 100 | done(); 101 | }) 102 | }); 103 | 104 | describe('addBoard', function () { 105 | var query; 106 | var post; 107 | 108 | beforeEach(function (done) { 109 | sinon.stub(restler, 'post').callsFake(function (uri, options) { 110 | return {once: function (event, callback) { 111 | callback(null, null); 112 | }}; 113 | }); 114 | 115 | trello.addBoard('name', 'description', 'organizationId', function () { 116 | query = restler.post.args[0][1].query; 117 | post = restler.post; 118 | done(); 119 | }); 120 | }); 121 | 122 | it('should post to https://api.trello.com/1/boards/', function () { 123 | 124 | post.should.have.been.calledWith('https://api.trello.com/1/boards/'); 125 | }); 126 | 127 | it('should include the description', function () { 128 | query.desc.should.equal('description'); 129 | }); 130 | 131 | it('should include the name', function () { 132 | query.name.should.equal('name'); 133 | }); 134 | 135 | it('should include the organization id', function () { 136 | query.idOrganization.should.equal('organizationId'); 137 | }); 138 | 139 | afterEach(function () { 140 | restler.post.restore(); 141 | }); 142 | }); 143 | 144 | describe('copyBoard', function () { 145 | var query; 146 | var post; 147 | 148 | beforeEach(function (done) { 149 | sinon.stub(restler, 'post').callsFake(function (uri, options) { 150 | return {once: function (event, callback) { 151 | callback(null, null); 152 | }}; 153 | }); 154 | 155 | trello.copyBoard('name', 'sourceBoardId', function () { 156 | query = restler.post.args[0][1].query; 157 | post = restler.post; 158 | done(); 159 | }); 160 | 161 | }); 162 | 163 | it('should post to https://api.trello.com/1/boards/', function () { 164 | post.should.have.been.calledWith('https://api.trello.com/1/boards/'); 165 | }); 166 | 167 | it('should include the name', function () { 168 | query.name.should.equal('name'); 169 | }); 170 | 171 | it('should include the source board id', function () { 172 | query.idBoardSource.should.equal('sourceBoardId'); 173 | }); 174 | 175 | afterEach(function () { 176 | restler.post.restore(); 177 | }); 178 | }); 179 | 180 | describe('addCard', function() { 181 | var query; 182 | var post; 183 | 184 | beforeEach(function (done) { 185 | sinon.stub(restler, 'post').callsFake(function (uri, options) { 186 | return {once: function (event, callback) { 187 | callback(null, null); 188 | }}; 189 | }); 190 | 191 | trello.addCard('name', 'description', 'listId', function () { 192 | query = restler.post.args[0][1].query; 193 | post = restler.post; 194 | done(); 195 | }); 196 | }); 197 | 198 | it('should post to https://api.trello.com/1/cards', function () { 199 | post.should.have.been.calledWith('https://api.trello.com/1/cards'); 200 | }); 201 | 202 | it('should include the description', function () { 203 | query.desc.should.equal('description'); 204 | }); 205 | 206 | it('should include the name', function () { 207 | query.name.should.equal('name'); 208 | }); 209 | 210 | it('should include the list id', function () { 211 | query.idList.should.equal('listId'); 212 | }); 213 | 214 | afterEach(function () { 215 | restler.post.restore(); 216 | }); 217 | }); 218 | 219 | describe('addCardWithExtraParams', function() { 220 | var query; 221 | var post; 222 | 223 | var extraParams = { 224 | desc: "description", 225 | due: new Date("2015/03/25"), 226 | dueComplete: false 227 | }; 228 | 229 | beforeEach(function (done) { 230 | sinon.stub(restler, 'post').callsFake(function (uri, options) { 231 | return {once: function (event, callback) { 232 | callback(null, null); 233 | }}; 234 | }); 235 | 236 | trello.addCardWithExtraParams('name', extraParams, 'listId', function () { 237 | query = restler.post.args[0][1].query; 238 | post = restler.post; 239 | done(); 240 | }); 241 | }); 242 | 243 | it('should post to https://api.trello.com/1/cards', function () { 244 | post.should.have.been.calledWith('https://api.trello.com/1/cards'); 245 | }); 246 | 247 | it('should include the name', function () { 248 | query.name.should.equal('name'); 249 | }); 250 | 251 | it('should include the description', function () { 252 | query.desc.should.equal('description'); 253 | }); 254 | 255 | it('should include the due date', function() { 256 | query.due.getTime().should.equal((new Date("2015/03/25").getTime())) 257 | }); 258 | 259 | it('should include the list id', function () { 260 | query.idList.should.equal('listId'); 261 | }); 262 | 263 | afterEach(function () { 264 | restler.post.restore(); 265 | }); 266 | 267 | }); 268 | 269 | describe('getCard', function() { 270 | var query; 271 | var post; 272 | 273 | beforeEach(function (done) { 274 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 275 | return {once: function (event, callback) { 276 | callback(null, null); 277 | }}; 278 | }); 279 | 280 | trello.getCard(null, 'cardId', function () { 281 | query = restler.get.args[0][1].query; 282 | get = restler.get; 283 | done(); 284 | }); 285 | }); 286 | 287 | it('should get https://api.trello.com/1/cards/cardId', function () { 288 | get.should.have.been.calledWith('https://api.trello.com/1/cards/cardId'); 289 | }); 290 | 291 | afterEach(function () { 292 | restler.get.restore(); 293 | }); 294 | 295 | }); 296 | 297 | describe('getCardFromBoard', function() { 298 | var query; 299 | var post; 300 | 301 | beforeEach(function (done) { 302 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 303 | return {once: function (event, callback) { 304 | callback(null, null); 305 | }}; 306 | }); 307 | 308 | trello.getCard('boardId', 'cardId', function () { 309 | query = restler.get.args[0][1].query; 310 | get = restler.get; 311 | done(); 312 | }); 313 | }); 314 | 315 | it('should get https://api.trello.com/1/boards/boardId/cards/cardId', function () { 316 | get.should.have.been.calledWith('https://api.trello.com/1/boards/boardId/cards/cardId'); 317 | }); 318 | 319 | afterEach(function () { 320 | restler.get.restore(); 321 | }); 322 | 323 | }); 324 | 325 | describe('getCardsOnListWithExtraParams', function () { 326 | var query; 327 | 328 | var testDate = new Date("2015/03/25"); 329 | var extraParams = {before: testDate} 330 | 331 | beforeEach(function (done) { 332 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 333 | return {once: function (event, callback) { 334 | callback(null, null); 335 | }}; 336 | }); 337 | 338 | trello.getCardsOnListWithExtraParams('listId', extraParams, function () { 339 | query = restler.get.args[0][1].query; 340 | get = restler.get; 341 | done(); 342 | }); 343 | }); 344 | 345 | it('should get from https://api.trello.com/1/lists/listId/cards', function () { 346 | get.should.have.been.calledWith('https://api.trello.com/1/lists/listId/cards'); 347 | }); 348 | it('should include a date in the query', function () { 349 | query.before.should.equal(testDate) 350 | }); 351 | 352 | afterEach(function () { 353 | restler.get.restore(); 354 | }); 355 | }); 356 | 357 | describe('getCardsOnBoardWithExtraParams', function () { 358 | var query; 359 | 360 | var testDate = new Date("2015/03/25"); 361 | var extraParams = {before: testDate} 362 | 363 | beforeEach(function (done) { 364 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 365 | return {once: function (event, callback) { 366 | callback(null, null); 367 | }}; 368 | }); 369 | 370 | trello.getCardsOnBoardWithExtraParams('boardId', extraParams, 'all', function () { 371 | query = restler.get.args[0][1].query; 372 | get = restler.get; 373 | done(); 374 | }); 375 | }); 376 | 377 | it('should include a date in the query', function () { 378 | query.before.should.equal(testDate) 379 | }); 380 | it('should get from https://api.trello.com/1/boards/boardId/cards', function () { 381 | get.should.have.been.calledWith('https://api.trello.com/1/boards/boardId/cards'); 382 | }); 383 | 384 | 385 | afterEach(function () { 386 | restler.get.restore(); 387 | }); 388 | }); 389 | 390 | describe('getListOnBoard', function() { 391 | beforeEach(function(done) { 392 | sinon.stub(restler, 'get').callsFake(function(uri, options) { 393 | return { once: function(event, callback) { 394 | callback(null, null); 395 | }} 396 | }); 397 | 398 | trello.getListsOnBoard('boardId', 'all', function () { 399 | get = restler.get; 400 | done(); 401 | }); 402 | }); 403 | 404 | it('should get from https://api.trello.com/1/boards/boardId/lists', function() { 405 | get.should.have.been.calledWith('https://api.trello.com/1/boards/boardId/lists'); 406 | }); 407 | 408 | afterEach(function() { 409 | restler.get.restore(); 410 | }) 411 | }); 412 | 413 | describe('addWebhook', function () { 414 | var query; 415 | var post; 416 | var data; 417 | 418 | beforeEach(function (done) { 419 | sinon.stub(restler, 'post').callsFake(function (uri, options) { 420 | return { 421 | once: function (event, callback) { 422 | callback(null, null); 423 | } 424 | }; 425 | }); 426 | 427 | trello.addWebhook('webhook', 'http://callback', 'xxx', function (result) { 428 | query = restler.post.args[0][1].query; 429 | data = restler.post.args[0][1].data; 430 | post = restler.post; 431 | done(); 432 | }); 433 | 434 | }); 435 | 436 | it('should post to https://api.trello.com/1/tokens/.../webhooks/', function () { 437 | 438 | post.args[0][0].should.equal('https://api.trello.com/1/tokens/' + trello.token + '/webhooks/'); 439 | }); 440 | 441 | it('should include the application key', function () { 442 | query.key.should.equal('key'); 443 | }); 444 | 445 | it('should include the decription', function () { 446 | data.description.should.equal('webhook'); 447 | }); 448 | 449 | it('should include the callbackUrl', function () { 450 | data.callbackURL.should.equal('http://callback'); 451 | }); 452 | 453 | it('should include the idModel', function () { 454 | data.idModel.should.equal('xxx'); 455 | }); 456 | 457 | afterEach(function () { 458 | restler.post.restore(); 459 | }); 460 | }); 461 | 462 | describe('deleteWebhook', function () { 463 | var query; 464 | var del; 465 | 466 | beforeEach(function (done) { 467 | sinon.stub(restler, 'delete').callsFake(function (uri, options) { 468 | return { 469 | once: function (event, callback) { 470 | callback(null, null); 471 | } 472 | }; 473 | }); 474 | 475 | trello.deleteWebhook('x1', function (result) { 476 | query = restler.delete.args[0][1].query; 477 | del = restler.delete; 478 | done(); 479 | }); 480 | 481 | }); 482 | 483 | it('should delete to https://api.trello.com/1/webhooks/x1', function () { 484 | del.args[0][0].should.equal('https://api.trello.com/1/webhooks/x1'); 485 | }); 486 | 487 | it('should include the application key', function () { 488 | query.key.should.equal('key'); 489 | }); 490 | 491 | afterEach(function () { 492 | restler.delete.restore(); 493 | }); 494 | }); 495 | 496 | 497 | describe('Update card list', function () { 498 | var query; 499 | var put; 500 | var data; 501 | 502 | beforeEach(function (done) { 503 | sinon.stub(restler, 'put').callsFake(function (uri, options) { 504 | return { 505 | once: function (event, callback) { 506 | callback(null, null); 507 | } 508 | }; 509 | }); 510 | 511 | trello.updateCardList('myCardId', 'newListId', function (result) { 512 | query = restler.put.args[0][1].query; 513 | put = restler.put; 514 | done(); 515 | }); 516 | 517 | }); 518 | 519 | it('should put to https://api.trello.com/1/cards/myCardId/idList', function () { 520 | put.args[0][0].should.equal('https://api.trello.com/1/cards/myCardId/idList'); 521 | }); 522 | 523 | it('should include the idList', function () { 524 | query.value.should.equal('newListId'); 525 | }); 526 | 527 | afterEach(function () { 528 | restler.put.restore(); 529 | }); 530 | }); 531 | 532 | describe.skip('It is able to chain several calls', function () { 533 | var trello, 534 | options = { 535 | key: "key", 536 | token: "tocken", 537 | listId: 'listId' 538 | }, 539 | cardsToCreate = 30, 540 | cardsCreated = 0; 541 | 542 | 543 | beforeEach(function (done) { 544 | var index, 545 | promisesList = [], 546 | cardCreationPromise = q.defer(); 547 | 548 | //Increase timeout due to real api calls 549 | this.timeout(1000000); 550 | 551 | //Setup a real trello client with a local configuration file 552 | if (fs.existsSync('./test/config.js')) { 553 | options = require('./config'); 554 | } 555 | trello = new Trello(options.key, options.token); 556 | 557 | 558 | //It creates and removes as many cards as set in cardsToCreate 559 | for(index = 0; index < cardsToCreate; index++) { 560 | cardCreationPromise = q.defer(); 561 | trello.addCard('Test card #' + index, 'test card', options.listId, cardCreationPromise.makeNodeResolver()); 562 | 563 | promisesList.push( 564 | //Remove the card created 565 | cardCreationPromise.promise.then(function (card) { 566 | if(!card.id) { 567 | console.log(card); 568 | throw "Trello call failed " + card; 569 | } else { 570 | var removeCardPromise = q.defer(); 571 | cardsCreated = cardsCreated + 1; 572 | trello.deleteCard(card.id, removeCardPromise.makeNodeResolver()); 573 | return removeCardPromise.promise; 574 | } 575 | }).fail (function(e){ 576 | throw "Trello call failed " + e; 577 | }) 578 | ); 579 | } 580 | 581 | q.allSettled(promisesList).then(function () { 582 | done(); 583 | }); 584 | }); 585 | 586 | it('should chain several calls without failing', function () { 587 | cardsCreated.should.equal(cardsToCreate); 588 | }); 589 | }); 590 | 591 | describe('addAttachmentToCard', function() { 592 | var query; 593 | var post; 594 | 595 | beforeEach(function (done) { 596 | sinon.stub(restler, 'post').callsFake(function (uri, options) { 597 | return {once: function (event, callback) { 598 | callback(null, null); 599 | }}; 600 | }); 601 | 602 | trello.addAttachmentToCard('myCardId', 'attachmentUrl', function () { 603 | query = restler.post.args[0][1].query; 604 | post = restler.post; 605 | done(); 606 | }); 607 | }); 608 | 609 | it("should post to the card's attachment URI", function () { 610 | post.should.have.been.calledWith('https://api.trello.com/1/cards/myCardId/attachments'); 611 | }); 612 | 613 | it('should include the attachmentUrl', function () { 614 | query.url.should.equal('attachmentUrl'); 615 | }); 616 | 617 | afterEach(function () { 618 | restler.post.restore(); 619 | }); 620 | }); 621 | 622 | describe('renameList', function() { 623 | var query; 624 | var put; 625 | 626 | beforeEach(function (done) { 627 | sinon.stub(restler, 'put').callsFake(function (uri, options) { 628 | return {once: function (event, callback) { 629 | callback(null, null); 630 | }}; 631 | }); 632 | 633 | trello.renameList('myListId', 'new list name', function () { 634 | query = restler.put.args[0][1].query; 635 | put = restler.put; 636 | done(); 637 | }); 638 | }); 639 | 640 | it("should PUT to the card's attachment URI", function () { 641 | put.should.have.been.calledWith('https://api.trello.com/1/lists/myListId/name'); 642 | }); 643 | 644 | it('should include the new list name', function () { 645 | query.value.should.equal('new list name'); 646 | }); 647 | 648 | afterEach(function () { 649 | restler.put.restore(); 650 | }); 651 | }); 652 | 653 | describe('returnPromise', function() { 654 | it('should return a promise', function() { 655 | var shouldBeAPromise = trello.addAttachmentToCard('myCardId', 'attachmentUrl'); 656 | 657 | shouldBeAPromise.should.be.a("Promise"); 658 | }); 659 | }); 660 | 661 | describe('executeCallback', function() { 662 | it('should not return a promise', function() { 663 | var shouldNotBeAPromise = trello.addAttachmentToCard('myCardId', 'attachmentUrl', function() {}); 664 | 665 | chai.assert.isUndefined(shouldNotBeAPromise); 666 | }); 667 | }); 668 | 669 | describe('getActionsOnBoard', function() { 670 | var get; 671 | 672 | beforeEach(function (done) { 673 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 674 | return {once: function (event, callback) { 675 | callback(null, null); 676 | }}; 677 | }); 678 | 679 | trello.getActionsOnBoard('boardId', function () { 680 | get = restler.get; 681 | done(); 682 | }); 683 | }); 684 | 685 | it('should get to https://api.trello.com/1/boards/boardId/actions', function () { 686 | get.should.have.been.calledWith('https://api.trello.com/1/boards/boardId/actions'); 687 | }); 688 | 689 | afterEach(function () { 690 | restler.get.restore(); 691 | }); 692 | }); 693 | 694 | describe('getCustomFieldsOnBoard', function() { 695 | var get; 696 | 697 | beforeEach(function (done) { 698 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 699 | return {once: function (event, callback) { 700 | callback(null, null); 701 | }}; 702 | }); 703 | 704 | trello.getCustomFieldsOnBoard('boardId', function () { 705 | get = restler.get; 706 | done(); 707 | }); 708 | }); 709 | 710 | it('should get to https://api.trello.com/1/boards/boardId/customFields', function () { 711 | get.should.have.been.calledWith('https://api.trello.com/1/boards/boardId/customFields'); 712 | }); 713 | 714 | afterEach(function () { 715 | restler.get.restore(); 716 | }); 717 | }); 718 | 719 | describe('getLabelsForBoard', function() { 720 | var get; 721 | 722 | beforeEach(function (done) { 723 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 724 | return {once: function (event, callback) { 725 | callback(null, null); 726 | }}; 727 | }); 728 | 729 | trello.getLabelsForBoard('boardId', function () { 730 | get = restler.get; 731 | done(); 732 | }); 733 | }); 734 | 735 | it('should get to https://api.trello.com/1/boards/boardId/labels', function () { 736 | get.should.have.been.calledWith('https://api.trello.com/1/boards/boardId/labels'); 737 | }); 738 | 739 | afterEach(function () { 740 | restler.get.restore(); 741 | }); 742 | }); 743 | 744 | describe('addMemberToBoard', function () { 745 | var data; 746 | var post; 747 | 748 | beforeEach(function (done) { 749 | sinon.stub(restler, 'put').callsFake(function (uri, options) { 750 | return {once: function (event, callback) { 751 | callback(null, null); 752 | }}; 753 | }); 754 | 755 | trello.addMemberToBoard('boardId', 'memberId', 'normal', function () { 756 | data = restler.put.args[0][1].data; 757 | put = restler.put; 758 | done(); 759 | }); 760 | }); 761 | 762 | it('should post to https://api.trello.com/1/boards/boardId/members/memberId', function () { 763 | put.should.have.been.calledWith('https://api.trello.com/1/boards/boardId/members/memberId'); 764 | }); 765 | 766 | it('should include the type', function () { 767 | data.type.should.equal('normal'); 768 | }); 769 | 770 | afterEach(function () { 771 | restler.put.restore(); 772 | }); 773 | }); 774 | 775 | describe('addLabelOnBoard', function() { 776 | var query; 777 | var data; 778 | var post; 779 | 780 | beforeEach(function (done) { 781 | sinon.stub(restler, 'post').callsFake(function (uri, options) { 782 | return {once: function (event, callback) { 783 | callback(null, null); 784 | }}; 785 | }); 786 | 787 | trello.addLabelOnBoard('boardId', 'name', 'color', function () { 788 | query = restler.post.args[0][1].query; 789 | data = restler.post.args[0][1].data; 790 | post = restler.post; 791 | done(); 792 | }); 793 | }); 794 | 795 | it('should post to https://api.trello.com/1/labels', function () { 796 | post.should.have.been.calledWith('https://api.trello.com/1/labels'); 797 | }); 798 | 799 | it('should include the color', function () { 800 | data.color.should.equal('color'); 801 | }); 802 | 803 | it('should include the name', function () { 804 | data.name.should.equal('name'); 805 | }); 806 | 807 | it('should include the board id', function () { 808 | data.idBoard.should.equal('boardId'); 809 | }); 810 | 811 | afterEach(function () { 812 | restler.post.restore(); 813 | }); 814 | }); 815 | 816 | describe('deleteLabel', function(){ 817 | var del; 818 | 819 | beforeEach(function (done) { 820 | sinon.stub(restler, 'delete').callsFake(function (uri, options) { 821 | return {once: function (event, callback) { 822 | callback(null, null); 823 | }}; 824 | }); 825 | 826 | trello.deleteLabel('labelId', function () { 827 | del = restler.delete; 828 | done(); 829 | }); 830 | }); 831 | 832 | it('should delete to https://api.trello.com/1/labels/labelId', function () { 833 | del.should.have.been.calledWith('https://api.trello.com/1/labels/labelId'); 834 | }); 835 | 836 | afterEach(function () { 837 | restler.delete.restore(); 838 | }); 839 | }); 840 | 841 | describe('addLabelToCard', function() { 842 | var query; 843 | var data; 844 | var post; 845 | 846 | beforeEach(function (done) { 847 | sinon.stub(restler, 'post').callsFake(function (uri, options) { 848 | return {once: function (event, callback) { 849 | callback(null, null); 850 | }}; 851 | }); 852 | 853 | trello.addLabelToCard('cardId', 'labelId', function () { 854 | query = restler.post.args[0][1].query; 855 | data = restler.post.args[0][1].data; 856 | post = restler.post; 857 | done(); 858 | }); 859 | }); 860 | 861 | it('should post to https://api.trello.com/1/cards/cardId/idLabels', function () { 862 | post.should.have.been.calledWith('https://api.trello.com/1/cards/cardId/idLabels'); 863 | }); 864 | 865 | it('should include the label id', function () { 866 | data.value.should.equal('labelId'); 867 | }); 868 | 869 | afterEach(function () { 870 | restler.post.restore(); 871 | }); 872 | }); 873 | 874 | describe('deleteLabelFromCard', function(){ 875 | var del; 876 | 877 | beforeEach(function (done) { 878 | sinon.stub(restler, 'delete').callsFake(function (uri, options) { 879 | return {once: function (event, callback) { 880 | callback(null, null); 881 | }}; 882 | }); 883 | 884 | trello.deleteLabelFromCard('cardId', 'labelId', function () { 885 | del = restler.delete; 886 | done(); 887 | }); 888 | }); 889 | 890 | it('should delete to https://api.trello.com/1/cards/cardId/idLabels/labelId', function () { 891 | del.should.have.been.calledWith('https://api.trello.com/1/cards/cardId/idLabels/labelId'); 892 | }); 893 | 894 | afterEach(function () { 895 | restler.delete.restore(); 896 | }); 897 | }); 898 | 899 | describe('updateLabel', function() { 900 | var query; 901 | var put; 902 | 903 | beforeEach(function (done) { 904 | sinon.stub(restler, 'put').callsFake(function (uri, options) { 905 | return {once: function (event, callback) { 906 | callback(null, null); 907 | }}; 908 | }); 909 | 910 | trello.updateLabel('labelId', 'field', 'value', function () { 911 | query = restler.put.args[0][1].query; 912 | put = restler.put; 913 | done(); 914 | }); 915 | }); 916 | 917 | it('should put to https://api.trello.com/1/labels/labelId/field', function () { 918 | put.should.have.been.calledWith('https://api.trello.com/1/labels/labelId/field'); 919 | }); 920 | 921 | it('should include the updated value', function () { 922 | query.value.should.equal('value'); 923 | }); 924 | 925 | afterEach(function () { 926 | restler.put.restore(); 927 | }); 928 | }); 929 | 930 | describe('getCardStickers', function() { 931 | var get; 932 | 933 | beforeEach(function (done) { 934 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 935 | return {once: function (event, callback) { 936 | callback(null, null); 937 | }}; 938 | }); 939 | 940 | trello.getCardStickers('cardId', function () { 941 | get = restler.get; 942 | done(); 943 | }); 944 | }); 945 | 946 | it('should get to https://api.trello.com/1/cards/cardId/stickers', function () { 947 | get.should.have.been.calledWith('https://api.trello.com/1/cards/cardId/stickers'); 948 | }); 949 | 950 | afterEach(function () { 951 | restler.get.restore(); 952 | }); 953 | }); 954 | 955 | describe('addStickerToCard', function() { 956 | var query; 957 | var data; 958 | var post; 959 | 960 | beforeEach(function (done) { 961 | sinon.stub(restler, 'post').callsFake(function (uri, options) { 962 | return {once: function (event, callback) { 963 | callback(null, null); 964 | }}; 965 | }); 966 | 967 | trello.addStickerToCard('cardId', 'image', 'left', 'top', 'zIndex', 'rotate', function () { 968 | query = restler.post.args[0][1].query; 969 | data = restler.post.args[0][1].data; 970 | post = restler.post; 971 | done(); 972 | }); 973 | }); 974 | 975 | it('should post to https://api.trello.com/1/cards/cardId/stickers', function () { 976 | post.should.have.been.calledWith('https://api.trello.com/1/cards/cardId/stickers'); 977 | }); 978 | 979 | it('should include the sticker image', function () { 980 | data.image.should.equal('image'); 981 | }); 982 | 983 | it('should include the sticker top', function () { 984 | data.top.should.equal('top'); 985 | }); 986 | 987 | it('should include the sticker left', function () { 988 | data.left.should.equal('left'); 989 | }); 990 | 991 | it('should include the sticker zIndex', function () { 992 | data.zIndex.should.equal('zIndex'); 993 | }); 994 | 995 | it('should include the sticker rotate', function () { 996 | data.rotate.should.equal('rotate'); 997 | }); 998 | 999 | afterEach(function () { 1000 | restler.post.restore(); 1001 | }); 1002 | }); 1003 | 1004 | describe('addChecklistToCard', function() { 1005 | var query; 1006 | var post; 1007 | 1008 | beforeEach(function (done) { 1009 | sinon.stub(restler, 'post').callsFake(function (uri, options) { 1010 | return {once: function (event, callback) { 1011 | callback(null, null); 1012 | }}; 1013 | }); 1014 | 1015 | trello.addChecklistToCard('cardId', 'name', function () { 1016 | query = restler.post.args[0][1].query; 1017 | post = restler.post; 1018 | done(); 1019 | }); 1020 | }); 1021 | 1022 | it('should post to https://api.trello.com/1/cards/cardId/checklists', function () { 1023 | post.should.have.been.calledWith('https://api.trello.com/1/cards/cardId/checklists'); 1024 | }); 1025 | 1026 | it('should include the checklist name', function () { 1027 | query.name.should.equal('name'); 1028 | }); 1029 | 1030 | afterEach(function () { 1031 | restler.post.restore(); 1032 | }); 1033 | }); 1034 | 1035 | describe('addExistingChecklistToCard', function() { 1036 | var query; 1037 | var post; 1038 | 1039 | beforeEach(function (done) { 1040 | sinon.stub(restler, 'post').callsFake(function (uri, options) { 1041 | return {once: function (event, callback) { 1042 | callback(null, null); 1043 | }}; 1044 | }); 1045 | 1046 | trello.addExistingChecklistToCard('cardId', 'checklistId', function () { 1047 | query = restler.post.args[0][1].query; 1048 | post = restler.post; 1049 | done(); 1050 | }); 1051 | }); 1052 | 1053 | it('should post to https://api.trello.com/1/cards/cardId/checklists', function () { 1054 | post.should.have.been.calledWith('https://api.trello.com/1/cards/cardId/checklists'); 1055 | }); 1056 | 1057 | it('should include the checklist ID', function () { 1058 | query.idChecklistSource.should.equal('checklistId'); 1059 | }); 1060 | 1061 | afterEach(function () { 1062 | restler.post.restore(); 1063 | }); 1064 | }); 1065 | 1066 | describe('getChecklistsOnCard', function() { 1067 | var get; 1068 | 1069 | beforeEach(function (done) { 1070 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 1071 | return {once: function (event, callback) { 1072 | callback(null, null); 1073 | }}; 1074 | }); 1075 | 1076 | trello.getChecklistsOnCard('cardId', function () { 1077 | get = restler.get; 1078 | done(); 1079 | }); 1080 | }); 1081 | 1082 | it('should get to https://api.trello.com/1/cards/cardId/checklists', function () { 1083 | get.should.have.been.calledWith('https://api.trello.com/1/cards/cardId/checklists'); 1084 | }); 1085 | 1086 | afterEach(function () { 1087 | restler.get.restore(); 1088 | }); 1089 | }); 1090 | 1091 | describe('getBoardMembers', function() { 1092 | var get; 1093 | 1094 | beforeEach(function (done) { 1095 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 1096 | return {once: function (event, callback) { 1097 | callback(null, null); 1098 | }}; 1099 | }); 1100 | 1101 | trello.getBoardMembers('boardId', function () { 1102 | get = restler.get; 1103 | done(); 1104 | }); 1105 | }); 1106 | 1107 | it('should get to https://api.trello.com/1/boards/boardId/members', function () { 1108 | get.should.have.been.calledWith('https://api.trello.com/1/boards/boardId/members'); 1109 | }); 1110 | 1111 | afterEach(function () { 1112 | restler.get.restore(); 1113 | }); 1114 | }); 1115 | 1116 | describe('getOrganization', function() { 1117 | var get; 1118 | 1119 | beforeEach(function (done) { 1120 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 1121 | return {once: function (event, callback) { 1122 | callback(null, null); 1123 | }}; 1124 | }); 1125 | 1126 | trello.getOrganization('organizationId', function () { 1127 | get = restler.get; 1128 | done(); 1129 | }); 1130 | }); 1131 | 1132 | it('should get to https://api.trello.com/1/organizations/organizationId', function () { 1133 | get.should.have.been.calledWith('https://api.trello.com/1/organizations/organizationId'); 1134 | }); 1135 | 1136 | afterEach(function () { 1137 | restler.get.restore(); 1138 | }); 1139 | }); 1140 | 1141 | describe('getOrgMembers', function() { 1142 | var get; 1143 | 1144 | beforeEach(function (done) { 1145 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 1146 | return {once: function (event, callback) { 1147 | callback(null, null); 1148 | }}; 1149 | }); 1150 | 1151 | trello.getOrgMembers('organizationId', function () { 1152 | get = restler.get; 1153 | done(); 1154 | }); 1155 | }); 1156 | 1157 | it('should get to https://api.trello.com/1/organizations/organizationId/members', function () { 1158 | get.should.have.been.calledWith('https://api.trello.com/1/organizations/organizationId/members'); 1159 | }); 1160 | 1161 | afterEach(function () { 1162 | restler.get.restore(); 1163 | }); 1164 | }); 1165 | 1166 | describe('getOrgBoards', function() { 1167 | var get; 1168 | 1169 | beforeEach(function (done) { 1170 | sinon.stub(restler, 'get').callsFake(function (uri, options) { 1171 | return {once: function (event, callback) { 1172 | callback(null, null); 1173 | }}; 1174 | }); 1175 | 1176 | trello.getOrgBoards('organizationId', function () { 1177 | get = restler.get; 1178 | done(); 1179 | }); 1180 | }); 1181 | 1182 | it('should get to https://api.trello.com/1/organizations/organizationId/boards', function () { 1183 | get.should.have.been.calledWith('https://api.trello.com/1/organizations/organizationId/boards'); 1184 | }); 1185 | 1186 | afterEach(function () { 1187 | restler.get.restore(); 1188 | }); 1189 | }); 1190 | 1191 | describe('updateChecklist', function() { 1192 | var query; 1193 | var put; 1194 | 1195 | beforeEach(function (done) { 1196 | sinon.stub(restler, 'put').callsFake(function (uri, options) { 1197 | return {once: function (event, callback) { 1198 | callback(null, null); 1199 | }}; 1200 | }); 1201 | 1202 | trello.updateChecklist('checklistId', 'field', 'value', function () { 1203 | query = restler.put.args[0][1].query; 1204 | put = restler.put; 1205 | done(); 1206 | }); 1207 | }); 1208 | 1209 | it('should put to https://api.trello.com/1/checklists/checklistId/field', function () { 1210 | put.should.have.been.calledWith('https://api.trello.com/1/checklists/checklistId/field'); 1211 | }); 1212 | 1213 | it('should include the checklist name', function () { 1214 | query.value.should.equal('value'); 1215 | }); 1216 | 1217 | afterEach(function () { 1218 | restler.put.restore(); 1219 | }); 1220 | }); 1221 | 1222 | describe('addDueDateToCard', function() { 1223 | var query; 1224 | var put; 1225 | 1226 | beforeEach(function (done) { 1227 | sinon.stub(restler, 'put').callsFake(function (uri, options) { 1228 | return {once: function (event, callback) { 1229 | callback(null, null); 1230 | }}; 1231 | }); 1232 | 1233 | trello.addDueDateToCard('cardId', 'value', function () { 1234 | query = restler.put.args[0][1].query; 1235 | put = restler.put; 1236 | done(); 1237 | }); 1238 | }); 1239 | 1240 | it('should put to https://api.trello.com/1/cards/cardId/due', function () { 1241 | put.should.have.been.calledWith('https://api.trello.com/1/cards/cardId/due'); 1242 | }); 1243 | 1244 | it('should include the date value', function () { 1245 | query.value.should.equal('value'); 1246 | }); 1247 | 1248 | afterEach(function () { 1249 | restler.put.restore(); 1250 | }); 1251 | }); 1252 | }); 1253 | --------------------------------------------------------------------------------