├── .esformatter ├── .gitignore ├── .jscsrc ├── LICENSE ├── README.md ├── coffeelint.json ├── gulpfile.js ├── package.json ├── public └── vso-client.js ├── spec └── vso-client.spec.js └── src └── vso-client.coffee /.esformatter: -------------------------------------------------------------------------------- 1 | { 2 | "preset":"jquery", 3 | "indent":{ 4 | "value":"\t", 5 | "ArrayExpression":1, 6 | "AssignmentExpression":1, 7 | "BinaryExpression":1, 8 | "ConditionalExpression":1, 9 | "CallExpression":1, 10 | "CatchClause":1, 11 | "DoWhileStatement":1, 12 | "ForInStatement":1, 13 | "ForStatement":1, 14 | "FunctionDeclaration":1, 15 | "FunctionExpression":1, 16 | "IfStatement":1, 17 | "MemberExpression":1, 18 | "MultipleVariableDeclaration":1, 19 | "ObjectExpression":1, 20 | "ReturnStatement":1, 21 | "SwitchCase":1, 22 | "SwitchStatement":1, 23 | "TopLevelFunctionBlock":1, 24 | "TryStatement":1, 25 | "WhileStatement":1 26 | }, 27 | "lineBreak":{ 28 | "value":"\n", 29 | "before":{ 30 | "AssignmentExpression":">=1", 31 | "AssignmentOperator":0, 32 | "BlockStatement":0, 33 | "CallExpression":-1, 34 | "ConditionalExpression":">=1", 35 | "CatchOpeningBrace":0, 36 | "CatchClosingBrace":">=1", 37 | "CatchKeyword":0, 38 | "DeleteOperator":">=1", 39 | "DoWhileStatement":">=1", 40 | "DoWhileStatementOpeningBrace":0, 41 | "DoWhileStatementClosingBrace":">=1", 42 | "EndOfFile":-1, 43 | "EmptyStatement":-1, 44 | "FinallyOpeningBrace":0, 45 | "FinallyClosingBrace":">=1", 46 | "ForInStatement":">=1", 47 | "ForInStatementExpressionOpening":0, 48 | "ForInStatementExpressionClosing":0, 49 | "ForInStatementOpeningBrace":0, 50 | "ForInStatementClosingBrace":">=1", 51 | "ForStatement":">=1", 52 | "ForStatementExpressionOpening":0, 53 | "ForStatementExpressionClosing":"<2", 54 | "ForStatementOpeningBrace":0, 55 | "ForStatementClosingBrace":">=1", 56 | "FunctionExpression":0, 57 | "FunctionExpressionOpeningBrace":0, 58 | "FunctionExpressionClosingBrace":">=1", 59 | "FunctionDeclaration":">=1", 60 | "FunctionDeclarationOpeningBrace":0, 61 | "FunctionDeclarationClosingBrace":">=1", 62 | "IfStatement":">=1", 63 | "IfStatementOpeningBrace":0, 64 | "IfStatementClosingBrace":">=1", 65 | "ElseIfStatement":0, 66 | "ElseIfStatementOpeningBrace":0, 67 | "ElseIfStatementClosingBrace":">=1", 68 | "ElseStatement":0, 69 | "ElseStatementOpeningBrace":0, 70 | "ElseStatementClosingBrace":">=1", 71 | "LogicalExpression":-1, 72 | "ObjectExpressionClosingBrace":-1, 73 | "Property":-1, 74 | "ReturnStatement":-1, 75 | "SwitchOpeningBrace":0, 76 | "SwitchClosingBrace":">=1", 77 | "ThisExpression":-1, 78 | "ThrowStatement":">=1", 79 | "TryOpeningBrace":0, 80 | "TryClosingBrace":">=1", 81 | "VariableName":">=1", 82 | "VariableValue":0, 83 | "VariableDeclaration":">=1", 84 | "VariableDeclarationWithoutInit":">=1", 85 | "WhileStatement":">=1", 86 | "WhileStatementOpeningBrace":0, 87 | "WhileStatementClosingBrace":">=1" 88 | }, 89 | "after":{ 90 | "AssignmentExpression":">=1", 91 | "AssignmentOperator":0, 92 | "BlockStatement":0, 93 | "CallExpression":-1, 94 | "CatchOpeningBrace":">=1", 95 | "CatchClosingBrace":">=0", 96 | "CatchKeyword":0, 97 | "ConditionalExpression":">=1", 98 | "DeleteOperator":">=1", 99 | "DoWhileStatement":">=1", 100 | "DoWhileStatementOpeningBrace":">=1", 101 | "DoWhileStatementClosingBrace":0, 102 | "EmptyStatement":-1, 103 | "FinallyOpeningBrace":">=1", 104 | "FinallyClosingBrace":">=1", 105 | "ForInStatement":">=1", 106 | "ForInStatementExpressionOpening":"<2", 107 | "ForInStatementExpressionClosing":-1, 108 | "ForInStatementOpeningBrace":">=1", 109 | "ForInStatementClosingBrace":">=1", 110 | "ForStatement":">=1", 111 | "ForStatementExpressionOpening":"<2", 112 | "ForStatementExpressionClosing":-1, 113 | "ForStatementOpeningBrace":">=1", 114 | "ForStatementClosingBrace":">=1", 115 | "FunctionExpression":">=1", 116 | "FunctionExpressionOpeningBrace":">=1", 117 | "FunctionExpressionClosingBrace":-1, 118 | "FunctionDeclaration":">=1", 119 | "FunctionDeclarationOpeningBrace":">=1", 120 | "FunctionDeclarationClosingBrace":">=1", 121 | "IfStatement":">=1", 122 | "IfStatementOpeningBrace":">=1", 123 | "IfStatementClosingBrace":">=1", 124 | "ElseIfStatement":">=1", 125 | "ElseIfStatementOpeningBrace":">=1", 126 | "ElseIfStatementClosingBrace":">=1", 127 | "ElseStatement":">=1", 128 | "ElseStatementOpeningBrace":">=1", 129 | "ElseStatementClosingBrace":">=1", 130 | "LogicalExpression":-1, 131 | "ObjectExpressionOpeningBrace":-1, 132 | "Property":0, 133 | "ReturnStatement":-1, 134 | "SwitchOpeningBrace":">=1", 135 | "SwitchClosingBrace":">=1", 136 | "ThisExpression":0, 137 | "ThrowStatement":">=1", 138 | "TryOpeningBrace":">=1", 139 | "TryClosingBrace":0, 140 | "VariableDeclaration":">=1", 141 | "WhileStatement":">=1", 142 | "WhileStatementOpeningBrace":">=1", 143 | "WhileStatementClosingBrace":">=1" 144 | } 145 | }, 146 | "whiteSpace":{ 147 | "value":" ", 148 | "removeTrailing":1, 149 | "before":{ 150 | "ArrayExpressionOpening":0, 151 | "ArrayExpressionClosing":1, 152 | "ArrayExpressionComma":0, 153 | "ArgumentComma":0, 154 | "ArgumentList":1, 155 | "ArgumentListArrayExpression":1, 156 | "ArgumentListFunctionExpression":1, 157 | "ArgumentListObjectExpression":1, 158 | "AssignmentOperator":1, 159 | "BinaryExpression":0, 160 | "BinaryExpressionOperator":1, 161 | "BlockComment":1, 162 | "CallExpression":-1, 163 | "CatchParameterList":0, 164 | "CatchOpeningBrace":1, 165 | "CatchClosingBrace":1, 166 | "CatchKeyword":1, 167 | "CommaOperator":0, 168 | "ConditionalExpressionConsequent":1, 169 | "ConditionalExpressionAlternate":1, 170 | "DoWhileStatementOpeningBrace":1, 171 | "DoWhileStatementClosingBrace":1, 172 | "DoWhileStatementConditional":1, 173 | "EmptyStatement":0, 174 | "ExpressionClosingParentheses":1, 175 | "FinallyOpeningBrace":1, 176 | "FinallyClosingBrace":1, 177 | "ForInStatement":1, 178 | "ForInStatementExpressionOpening":1, 179 | "ForInStatementExpressionClosing":0, 180 | "ForInStatementOpeningBrace":1, 181 | "ForInStatementClosingBrace":1, 182 | "ForStatement":1, 183 | "ForStatementExpressionOpening":1, 184 | "ForStatementExpressionClosing":0, 185 | "ForStatementOpeningBrace":1, 186 | "ForStatementClosingBrace":1, 187 | "ForStatementSemicolon":0, 188 | "FunctionDeclarationOpeningBrace":1, 189 | "FunctionDeclarationClosingBrace":1, 190 | "FunctionExpressionOpeningBrace":1, 191 | "FunctionExpressionClosingBrace":1, 192 | "IfStatementConditionalOpening":1, 193 | "IfStatementConditionalClosing":1, 194 | "IfStatementOpeningBrace":1, 195 | "IfStatementClosingBrace":1, 196 | "ElseStatementOpeningBrace":1, 197 | "ElseStatementClosingBrace":1, 198 | "ElseIfStatementOpeningBrace":1, 199 | "ElseIfStatementClosingBrace":1, 200 | "MemberExpressionClosing":1, 201 | "LineComment":1, 202 | "LogicalExpressionOperator":1, 203 | "ObjectExpressionClosingBrace":1, 204 | "Property":1, 205 | "PropertyValue":1, 206 | "ParameterComma":0, 207 | "ParameterList":1, 208 | "SwitchDiscriminantOpening":1, 209 | "SwitchDiscriminantClosing":0, 210 | "ThrowKeyword":1, 211 | "TryOpeningBrace":1, 212 | "TryClosingBrace":1, 213 | "UnaryExpressionOperator":0, 214 | "VariableName":1, 215 | "VariableValue":1, 216 | "WhileStatementConditionalOpening":1, 217 | "WhileStatementConditionalClosing":0, 218 | "WhileStatementOpeningBrace":1, 219 | "WhileStatementClosingBrace":1 220 | }, 221 | "after":{ 222 | "ArrayExpressionOpening":1, 223 | "ArrayExpressionClosing":0, 224 | "ArrayExpressionComma":1, 225 | "ArgumentComma":1, 226 | "ArgumentList":1, 227 | "ArgumentListArrayExpression":1, 228 | "ArgumentListFunctionExpression":1, 229 | "ArgumentListObjectExpression":1, 230 | "AssignmentOperator":1, 231 | "BinaryExpression":0, 232 | "BinaryExpressionOperator":1, 233 | "BlockComment":1, 234 | "CallExpression":0, 235 | "CatchParameterList":0, 236 | "CatchOpeningBrace":1, 237 | "CatchClosingBrace":1, 238 | "CatchKeyword":1, 239 | "CommaOperator":1, 240 | "ConditionalExpressionConsequent":1, 241 | "ConditionalExpressionTest":1, 242 | "DoWhileStatementOpeningBrace":1, 243 | "DoWhileStatementClosingBrace":1, 244 | "DoWhileStatementBody":1, 245 | "EmptyStatement":0, 246 | "ExpressionOpeningParentheses":1, 247 | "FinallyOpeningBrace":1, 248 | "FinallyClosingBrace":1, 249 | "ForInStatement":1, 250 | "ForInStatementExpressionOpening":0, 251 | "ForInStatementExpressionClosing":1, 252 | "ForInStatementOpeningBrace":1, 253 | "ForInStatementClosingBrace":1, 254 | "ForStatement":1, 255 | "ForStatementExpressionOpening":0, 256 | "ForStatementExpressionClosing":1, 257 | "ForStatementClosingBrace":1, 258 | "ForStatementOpeningBrace":1, 259 | "ForStatementSemicolon":1, 260 | "FunctionReservedWord":0, 261 | "FunctionName":0, 262 | "FunctionExpressionOpeningBrace":1, 263 | "FunctionExpressionClosingBrace":0, 264 | "FunctionDeclarationOpeningBrace":1, 265 | "FunctionDeclarationClosingBrace":1, 266 | "IfStatementConditionalOpening":1, 267 | "IfStatementConditionalClosing":1, 268 | "IfStatementOpeningBrace":1, 269 | "IfStatementClosingBrace":1, 270 | "ElseStatementOpeningBrace":1, 271 | "ElseStatementClosingBrace":1, 272 | "ElseIfStatementOpeningBrace":1, 273 | "ElseIfStatementClosingBrace":1, 274 | "MemberExpressionOpening":1, 275 | "LogicalExpressionOperator":1, 276 | "ObjectExpressionClosingBrace":0, 277 | "PropertyName":0, 278 | "PropertyValue":0, 279 | "ParameterComma":1, 280 | "ParameterList":1, 281 | "SwitchDiscriminantOpening":0, 282 | "SwitchDiscriminantClosing":1, 283 | "ThrowKeyword":1, 284 | "TryOpeningBrace":1, 285 | "TryClosingBrace":1, 286 | "UnaryExpressionOperator":0, 287 | "VariableName":1, 288 | "WhileStatementConditionalOpening":0, 289 | "WhileStatementConditionalClosing":1, 290 | "WhileStatementOpeningBrace":1, 291 | "WhileStatementClosingBrace":1 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | .DS_Store 4 | *.log 5 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "disallowKeywords": [ 3 | "with" 4 | ], 5 | "disallowKeywordsOnNewLine": [ 6 | "else" 7 | ], 8 | "disallowMixedSpacesAndTabs": "smart", 9 | "disallowMultipleLineBreaks": true, 10 | "disallowMultipleLineStrings": true, 11 | "disallowMultipleSpaces": true, 12 | "disallowMultipleVarDecl": "exceptUndefined", 13 | "disallowNewlineBeforeBlockStatements": true, 14 | "disallowOperatorBeforeLineBreak": [ 15 | "+", 16 | "." 17 | ], 18 | "disallowPaddingNewlinesInBlocks": true, 19 | "disallowQuotedKeysInObjects": "allButReserved", 20 | "disallowSpaceAfterObjectKeys": true, 21 | "disallowSpaceAfterPrefixUnaryOperators": true, 22 | "disallowSpaceBeforeBinaryOperators": [ 23 | ",", 24 | ":" 25 | ], 26 | "disallowSpaceBeforePostfixUnaryOperators": true, 27 | "disallowSpacesInAnonymousFunctionExpression": { 28 | "beforeOpeningRoundBrace": true 29 | }, 30 | "disallowSpacesInCallExpression": true, 31 | "disallowSpacesInFunctionExpression": { 32 | "beforeOpeningRoundBrace": true 33 | }, 34 | "disallowSpacesInNamedFunctionExpression": { 35 | "beforeOpeningRoundBrace": true 36 | }, 37 | "disallowTrailingComma": true, 38 | "disallowTrailingWhitespace": true, 39 | "excludeFiles": [ 40 | "node_modules/**" 41 | ], 42 | "maximumLineLength": null, 43 | "requireBlocksOnNewline": true, 44 | "requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties", 45 | "requireCapitalizedConstructors": true, 46 | "requireCommaBeforeLineBreak": true, 47 | "requireCurlyBraces": [ 48 | "if", 49 | "else", 50 | "for", 51 | "while", 52 | "do", 53 | "try", 54 | "catch" 55 | ], 56 | "requireDotNotation": "except_snake_case", 57 | "requireLineFeedAtFileEnd": true, 58 | "requireOperatorBeforeLineBreak": true, 59 | "requirePaddingNewLinesBeforeLineComments": null, 60 | "requireParenthesesAroundIIFE": true, 61 | "requireSemicolons": true, 62 | "requireSpaceAfterBinaryOperators": true, 63 | "requireSpaceAfterKeywords": [ 64 | "if", 65 | "else", 66 | "for", 67 | "while", 68 | "do", 69 | "switch", 70 | "return", 71 | "try", 72 | "catch" 73 | ], 74 | "requireSpaceBeforeBinaryOperators": [ 75 | "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "|=", "^=", "+=", "+", "-", 76 | "*", "/", "%", "<<", ">>", ">>>", "&", "|", "^", "&&", "||", "===", "==", ">=", "<=", "<", ">", "!=", "!==" 77 | ], 78 | "requireSpaceBeforeBlockStatements": true, 79 | "requireSpaceBeforeObjectValues": true, 80 | "requireSpaceBetweenArguments": true, 81 | "requireSpacesInAnonymousFunctionExpression": { 82 | "beforeOpeningCurlyBrace": true 83 | }, 84 | "requireSpacesInConditionalExpression": true, 85 | "requireSpacesInForStatement": true, 86 | "requireSpacesInFunctionExpression": { 87 | "beforeOpeningCurlyBrace": true 88 | }, 89 | "requireSpacesInNamedFunctionExpression": { 90 | "beforeOpeningCurlyBrace": true 91 | }, 92 | "requireSpacesInsideArrayBrackets": "all", 93 | "requireSpacesInsideObjectBrackets": "all", 94 | "requireSpacesInsideParentheses": "all", 95 | "safeContextKeyword": "that", 96 | "validateIndentation": "\t", 97 | "validateLineBreaks": "LF", 98 | "validateQuoteMarks": { 99 | "escape": true, 100 | "mark": "\"" 101 | } 102 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 David Neal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Visual Studio Online Node.js Client 2 | 3 | The VSO client module for Node.js is a wrapper for the latest [Visual Studio Online REST API](http://www.visualstudio.com/integrate/reference/reference-vso-overview-vsi). It provides an easy-to-use set of functions designed to simplify the integration of external systems and utilities with your Visual Studio Online account. 4 | 5 | Features: 6 | * Get a list of projects, collections, teams, and team members 7 | * Manage Team Rooms, membership and messages 8 | * Query for work items using saved queries or WIQL 9 | * Create and update work items, including attachments 10 | * Manage saved queries 11 | * TFS version control, branches, changesets, shelvesets, and labels 12 | * Git repositories, commits, diffs, pushes, stats and refs 13 | * Service hook publishers, consumers, and subscriptions 14 | 15 | ### Requirements 16 | 17 | * [Node.js](http://nodejs.org) 18 | * A [Visual Studio Online](https://visualstudio.com) account 19 | 20 | ### Installing the client 21 | 22 | npm install vso-client 23 | 24 | ### Client usage 25 | 26 | You can authenticate with Visual Studio Online using [basic authentication](http://www.visualstudio.com/integrate/get-started/get-started-auth-introduction-vsi) or [OAuth 2.0](http://www.visualstudio.com/integrate/get-started/get-started-auth-oauth2-vsi). 27 | 28 | #### Basic Authentication 29 | 30 | You must first enable [alternate credentials](http://www.visualstudio.com/integrate/get-started/get-started-auth-introduction-vsi) on your profile. 31 | 32 | var vso = require('vso-client'); 33 | var client = vso.createClient('url', 'collection', 'your-username', 'your-p@ssw0rd'); 34 | 35 | client.getProjects(function(err, projects) { 36 | if (err) { 37 | console.log(err); 38 | } else { 39 | console.log(projects); 40 | } 41 | }); 42 | 43 | #### OAuth 2.0 44 | 45 | **Step 1: Authorize application** 46 | 47 | To use OAuth 2.0, you must follow the steps described in [Authorize access with OAuth 2.0](http://www.visualstudio.com/integrate/get-started/get-started-auth-oauth2-vsi). 48 | 49 | **Step 2: Request access token** 50 | 51 | Use the vso-client to request or renew an access token. 52 | 53 | var vso = require('vso-client'); 54 | 55 | vso.getToken ('clientAssertion', 'assertion', 'redirectUri', function(err, response) { 56 | if(err) { 57 | console.log(err); 58 | } else { 59 | if (typeof (response.Error) !== "undefined") { 60 | console.log("No token. Returned message was: " + response); 61 | } else { 62 | console.log("Received Token: " + response.access_token) 63 | } 64 | } 65 | } 66 | 67 | Note: Use `refreshToken` to renew an existing access token. 68 | 69 | **Step 3: Initialize client with access token** 70 | 71 | var client = vso.createOAuthClient('url', 'collection', 'access token'); 72 | 73 | client.getProjects(function(err, projects) { 74 | if (err) { 75 | console.log(err); 76 | } else { 77 | console.log(projects); 78 | } 79 | }); 80 | 81 | ## Client Options 82 | 83 | The VSO client supports a few optional settings, passed as an `options` object when you create the client (The last parameter of createClient and createOAuthClient) 84 | 85 | var options = { 86 | apiVersion: "1.0", 87 | userAgent: "My App 1.0", 88 | clientOptions: { 89 | proxy: "http://localproxy.com" 90 | } 91 | } 92 | var vso = require('vso-client'); 93 | var client = vso.createClient('url', 'collection', 'your-username', 'your-p@ssw0rd', options); 94 | 95 | 96 | ### API Versioning 97 | 98 | Visual Studio Online API are [versioned](http://www.visualstudio.com/integrate/get-started/get-started-rest-basics-vsi#versioning) to ensure client applications keep working as expect when a new version of the API comes out. 99 | 100 | When you create a client using `createClient` or `createOAuthClient` you can explicitly specify the API version you wish to use. 101 | 102 | If you don't explicitly specify the version you want to use, the latest version will be used by default. Since it is up to the caller to pass the right parameters (and interpret the results) to the methods it calls, it is therefore recommended to explicitly pass a version when you create a client. 103 | 104 | In can specify the version by passing the apiVersion member in the options parameter. 105 | 106 | An example with the `createClient` (it works the same with `createOAuthClient`) 107 | 108 | var vso = require('vso-client'); 109 | var client = vso.createClient('url', 'collection', 'your-username', 'your-p@ssw0rd', {apiVersion : "1.0-preview.1"}); 110 | 111 | ### Custom User Agent 112 | 113 | By default, the user agent string used by the client when making requests is `vso-client/{version} node/{version} {os}`. You can specify a custom application name to append to the user-agent string. 114 | 115 | var vso = require('vso-client'); 116 | var client = vso.createClient('url', 'collection', 'your-username', 'your-p@ssw0rd', {userAgent : "My-App/1.0"}); 117 | 118 | The application name shouldn't contain spaces and if you want to specify a version name, it shouldn't be part of the application name but separated with a slash. 119 | 120 | Althoug VSO client doesn't enforce the product name for user agent you should follow the rules explained in [RFC 7231](http://tools.ietf.org/html/rfc7231#page-46) 121 | 122 | ### Other Request Options 123 | 124 | You can pass other client options to be used by the internal [request](https://www.npmjs.com/package/request) client by using the `clientOptions` property of the options object, such as a proxy server to use for requests. 125 | 126 | var requestOptions = { proxy: 'http://localproxy.com' }; 127 | var vso = require('vso-client'); 128 | var client = vso.createClient('url', 'collection', 'your-username', 'your-p@ssw0rd', { clientOptions: requestOptions }); 129 | 130 | ## API Reference 131 | 132 | Review the [tests](https://github.com/leankit-labs/vso-client/blob/master/spec/vso-client.spec.js) for a full list of client functions and their usuage. Also, refer to the [Visual Studio Online REST API](http://www.visualstudio.com/integrate/reference/reference-vso-overview-vsi) documentation. 133 | 134 | ## Developing from Source 135 | 136 | The VSO client is written in [CoffeeScript](http://coffeescript.org/). To modify and rebuild the client from source, you will need the CoffeeScript compiler. [Gulp.js](http://gulpjs.com/) is used to automatically build the client and run all [mocha](http://mochajs.org/) tests. 137 | 138 | * Clone or download the `vso-client` Github repository. 139 | * Open a Terminal window, change to the repository folder, and install dependent packages. 140 | 141 | npm install -g coffee-script 142 | npm install -g gulp 143 | npm install -g mocha 144 | npm install -g should 145 | npm install 146 | npm install --package-dev 147 | 148 | * Add environment variables for integration tests 149 | 150 | **OS X / Linux** 151 | 152 | export VSO_URL=your-vso-account-URL 153 | export VSO_COLLECTION=your-project-collection 154 | export VSO_USER=your-username 155 | export VSO_PWD=your-password 156 | export VSO_SERVICE_ACCOUNT_USER=service-account-username 157 | export VSO_SERVICE_ACCOUNT_PWD=service-account-password 158 | 159 | 160 | **Windows** 161 | 162 | setx VSO_URL "your-vso-account-URL" 163 | setx VSO_COLLECTION "your-project-collection" 164 | setx VSO_USER "your-username" 165 | setx VSO_PWD "your-password" 166 | setx VSO_SERVICE_ACCOUNT_USER=service-account-username 167 | setx VSO_SERVICE_ACCOUNT_PWD=service-account-password 168 | 169 | 170 | **Notes** 171 | - On Windows, you will need to reopen your command prompt after setting environment variables. 172 | - To get a Visual Studio Online service account you can use the [TFS Service Credential Viewer](http://nakedalm.com/getting-service-account-vso-tfs-service-credential-viewer/) 173 | 174 | * Keep `gulp` running to compile the `/src` folder and run mocha tests in the `/spec` folder. 175 | 176 | gulp 177 | 178 | ### Installing manually 179 | 180 | * Create a folder in your node application's `node_modules` folder named `vso-client` (e.g. `[project-name]/node_modules/vso-client). 181 | * Copy all the files and folders in the `vso-client` project folder to the `vso-client` folder created in the previous step. 182 | 183 | ### License 184 | 185 | The Visual Studio Online client module is licensed under [MIT](http://www.opensource.org/licenses/mit-license.php). Refer to [license.txt](https://github.com/leankit-labs/vso-client/blob/master/LICENSE) for more information. 186 | -------------------------------------------------------------------------------- /coffeelint.json: -------------------------------------------------------------------------------- 1 | { 2 | "max_line_length": { 3 | "name": "max_line_length", 4 | "level": "ignore" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require( "gulp" ); 2 | var gutil = require( "gulp-util" ); 3 | var mocha = require( "gulp-mocha" ); 4 | var coffee = require( "gulp-coffee" ); 5 | 6 | gulp.task( "coffee", function() { 7 | return gulp.src( "./src/*.coffee" ) 8 | .pipe( coffee( { bare: true } ).on( "error", gutil.log ) ) 9 | .pipe( gulp.dest( "./public/" ) ); 10 | } ); 11 | 12 | gulp.task( "test", [ "coffee" ], function() { 13 | return gulp.src( "./spec/*.spec.js" ) 14 | .pipe( mocha( { reporter: "spec" } ) ) 15 | .on( "error", gutil.log ); 16 | } ); 17 | 18 | gulp.task( "watch", function() { 19 | gulp.watch( [ "./spec/*.spec.js", "./src/*.coffee" ], [ "test" ] ); 20 | } ); 21 | 22 | gulp.task( "default", [ "test", "watch" ], function() { 23 | } ); 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vso-client", 3 | "version": "0.2.5", 4 | "description": "Visual Studio Online Client", 5 | "main": "./public/vso-client.js", 6 | "scripts": { 7 | "test": "gulp" 8 | }, 9 | "author": "David Neal (http://reverentgeek.com)", 10 | "contributors": [ 11 | { 12 | "name": "Tiago Pascoal" 13 | }, 14 | { 15 | "name": "Rob Maher" 16 | } 17 | ], 18 | "license": "MIT", 19 | "devDependencies": { 20 | "async": "^0.9.0", 21 | "coffee-script": "~1.7.1", 22 | "gulp": "~3.6.2", 23 | "gulp-coffee": "~1.4.3", 24 | "gulp-mocha": "~0.4.1", 25 | "gulp-util": "~2.2.14", 26 | "mocha": "~1.18.2", 27 | "should": "~3.3.1" 28 | }, 29 | "dependencies": { 30 | "azure": "^0.9.16", 31 | "lodash": "~2.4.1", 32 | "request": "~2.48.0", 33 | "request-json": "~0.5.0" 34 | }, 35 | "bugs": { 36 | "url": "https://github.com/LeanKit-Labs/vso-client/issues", 37 | "email": "david@reverentgeek.com" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "https://github.com/LeanKit-Labs/vso-client" 42 | }, 43 | "keywords": [ 44 | "tfs", 45 | "visualstudio", 46 | "visualstudioonline" 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /public/vso-client.js: -------------------------------------------------------------------------------- 1 | var AuthenticationCredential, AuthenticationOAuth, AuthenticationWrap, apiVersion, azure, request, requestJson, requestToken, requestWrapToken, spsUri, vsoTokenUri, _; 2 | 3 | _ = require('lodash'); 4 | 5 | requestJson = require('request-json'); 6 | 7 | request = require("request"); 8 | 9 | azure = require('azure'); 10 | 11 | apiVersion = '1.0'; 12 | 13 | spsUri = 'https://app.vssps.visualstudio.com'; 14 | 15 | vsoTokenUri = spsUri + '/oauth2/token'; 16 | 17 | requestToken = function(clientAssertion, assertion, grantType, redirectUri, callback, tokenUri) { 18 | return request.post(tokenUri, { 19 | form: { 20 | "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", 21 | "client_assertion": clientAssertion, 22 | "response_type": "Assertion", 23 | "grant_type": grantType, 24 | "assertion": assertion, 25 | "redirect_uri": redirectUri 26 | } 27 | }, function(err, res, body) { 28 | if (err) { 29 | return callback(err, body); 30 | } else if (res.statusCode !== 200 && res.statusCode !== 400 && res.statusCode !== 401) { 31 | return callback("Error Code " + res.statusCode, body); 32 | } else { 33 | return callback(err, JSON.parse(body)); 34 | } 35 | }); 36 | }; 37 | 38 | requestWrapToken = function(accountUrl, username, password, callback) { 39 | return request({ 40 | url: accountUrl, 41 | followRedirect: false 42 | }, function(err, res, body) { 43 | var issuer, realm, wrapService; 44 | if (err) { 45 | return callback(err, body, res); 46 | } 47 | realm = res.headers['x-tfs-fedauthrealm']; 48 | issuer = res.headers['x-tfs-fedauthissuer']; 49 | if (!(realm && issuer)) { 50 | return callback("Can't determine Federation data on headers", body, res); 51 | } 52 | wrapService = azure.createWrapService(issuer, username, password); 53 | return wrapService.wrapAccessToken(realm, function(err, token, res) { 54 | return callback(err, (err ? res.body : token), res); 55 | }); 56 | }); 57 | }; 58 | 59 | exports.createClient = function(url, collection, username, password, options) { 60 | return new exports.Client(url, collection, new AuthenticationCredential(username, password), options); 61 | }; 62 | 63 | exports.createOAuthClient = function(url, collection, accessToken, options) { 64 | return new exports.Client(url, collection, new AuthenticationOAuth(accessToken), options); 65 | }; 66 | 67 | exports.createWrapClient = function(url, collection, accessToken, options) { 68 | return new exports.Client(url, collection, new AuthenticationWrap(accessToken), options); 69 | }; 70 | 71 | exports.getToken = function(clientAssertion, assertion, redirectUri, callback, tokenUri) { 72 | if (tokenUri == null) { 73 | tokenUri = vsoTokenUri; 74 | } 75 | return requestToken(clientAssertion, assertion, "urn:ietf:params:oauth:grant-type:jwt-bearer", redirectUri, callback, tokenUri); 76 | }; 77 | 78 | exports.refreshToken = function(clientAssertion, assertion, redirectUri, callback, tokenUri) { 79 | if (tokenUri == null) { 80 | tokenUri = vsoTokenUri; 81 | } 82 | return requestToken(clientAssertion, assertion, "refresh_token", redirectUri, callback, tokenUri); 83 | }; 84 | 85 | exports.getWrapToken = function(accountUrl, username, password, callback) { 86 | return requestWrapToken(accountUrl, username, password, callback); 87 | }; 88 | 89 | AuthenticationCredential = (function() { 90 | function AuthenticationCredential(username, password) { 91 | this.username = username; 92 | this.password = password; 93 | this.type = "Credential"; 94 | } 95 | 96 | return AuthenticationCredential; 97 | 98 | })(); 99 | 100 | AuthenticationOAuth = (function() { 101 | function AuthenticationOAuth(accessToken) { 102 | this.accessToken = accessToken; 103 | this.type = "OAuth"; 104 | } 105 | 106 | return AuthenticationOAuth; 107 | 108 | })(); 109 | 110 | AuthenticationWrap = (function() { 111 | function AuthenticationWrap(accessToken) { 112 | this.accessToken = accessToken; 113 | this.type = "Wrap"; 114 | } 115 | 116 | return AuthenticationWrap; 117 | 118 | })(); 119 | 120 | exports.Client = (function() { 121 | var getMinorVersion, getVersion, getVersionStage; 122 | 123 | function Client(url, collection, authentication, options) { 124 | var apiUrl, spsUrl, userAgent; 125 | this.url = url; 126 | this.collection = collection; 127 | apiUrl = url; 128 | this.client = requestJson.newClient(apiUrl, options != null ? options.clientOptions : void 0); 129 | if (authentication === AuthenticationCredential || authentication.type === "Credential") { 130 | this.client.setBasicAuth(authentication.username, authentication.password); 131 | } else if (authentication === AuthenticationOAuth || authentication.type === "OAuth") { 132 | spsUrl = (options != null ? options.spsUri : void 0) || spsUri; 133 | this.clientSPS = requestJson.newClient(spsUrl, options != null ? options.clientOptions : void 0); 134 | this.client.headers.Authorization = "bearer " + authentication.accessToken; 135 | this.clientSPS.headers.Authorization = "bearer " + authentication.accessToken; 136 | } else if (authentication === AuthenticationWrap || authentication.type === "Wrap") { 137 | this.client.headers.Authorization = "WRAP access_token=\"" + authentication.accessToken + "\""; 138 | } else { 139 | throw new Error("unknown authentication type"); 140 | } 141 | this._authType = authentication.type; 142 | this.apiVersion = (options != null ? options.apiVersion : void 0) || apiVersion; 143 | userAgent = 'vso-client/1.0 node/' + process.versions.node + ' ' + process.platform; 144 | if (((options != null ? options.userAgent : void 0) != null)) { 145 | this.userAgent = options.userAgent + '; ' + userAgent; 146 | } else { 147 | this.userAgent = userAgent; 148 | } 149 | } 150 | 151 | Client.prototype.parseReplyData = function(error, res, body, callback) { 152 | var err; 153 | if (error) { 154 | return callback(error, body); 155 | } else if (this._authType === "OAuth" && (res != null ? res.statusCode : void 0) === 203) { 156 | return callback("Error unauthorized. Check OAUth token", body); 157 | } else if ((res != null ? res.statusCode : void 0) === 401 || (this._authType !== "OAuth" && (res != null ? res.statusCode : void 0) === 203)) { 158 | return callback("Error unauthorized", body); 159 | } else if ((res != null ? res.statusCode : void 0) >= 500 && (res != null ? res.statusCode : void 0) < 600) { 160 | return callback("Error call failed with HTTP Code " + res.statusCode, body); 161 | } else if ((body.errorCode || body.errorCode === 0) && (body.message || body.typeKey)) { 162 | err = 'Error ' + body.errorCode + ': '; 163 | if (body.message && body.message.length > 0) { 164 | err += body.message; 165 | } else { 166 | err += body.typeKey; 167 | } 168 | return callback(err, body); 169 | } else if (body && body.value) { 170 | return callback(error, body.value); 171 | } else if (body && body.id) { 172 | return callback(error, body); 173 | } else if (body && body.length > 0) { 174 | return callback('Unknown Error', body); 175 | } else { 176 | return callback(error, body); 177 | } 178 | }; 179 | 180 | Client.prototype.findItemField = function(fields, fieldName) { 181 | var field; 182 | field = _.find(fields, function(f) { 183 | return f.field.refName === fieldName; 184 | }); 185 | return field; 186 | }; 187 | 188 | Client.prototype.findFirstItemField = function(fields, fieldNames) { 189 | var field, fieldName, _i, _len; 190 | field = null; 191 | for (_i = 0, _len = fieldNames.length; _i < _len; _i++) { 192 | fieldName = fieldNames[_i]; 193 | field = this.findItemField(fields, fieldName); 194 | if (field) { 195 | break; 196 | } 197 | } 198 | return field; 199 | }; 200 | 201 | Client.prototype.setAccessToken = function(acessToken) { 202 | if (this._authType !== "OAuth" && this._authType !== "Wrap") { 203 | throw new Error("can only set access token for OAuth or Wrap client"); 204 | } 205 | if (this._authType === "OAuth") { 206 | return this.client.headers.Authorization = "bearer " + acessToken; 207 | } else { 208 | return this.client.headers.Authorization = "WRAP access_token=\"" + acessToken + "\""; 209 | } 210 | }; 211 | 212 | Client.prototype.setVersion = function(version) { 213 | return this.apiVersion = version; 214 | }; 215 | 216 | Client.prototype.checkAndRequireOAuth = function(methodName) { 217 | if (this._authType !== "OAuth") { 218 | throw new Error(methodName + " can only be invoked with OAuth"); 219 | } 220 | }; 221 | 222 | Client.prototype.buildApiPath = function(path, params, options) { 223 | var basePath, returnPath; 224 | basePath = ""; 225 | if (!(options != null ? options.excludeCollection : void 0)) { 226 | if (options != null ? options.projectName : void 0) { 227 | basePath = '/' + this.collection + '/' + (encodeURI(options.projectName)); 228 | } else { 229 | basePath = '/' + this.collection; 230 | } 231 | } 232 | returnPath = basePath + '/_apis/' + path; 233 | if (params && params.length > 0) { 234 | if (params[0] !== '?') { 235 | params = '?' + params; 236 | } 237 | returnPath += params; 238 | } 239 | return returnPath; 240 | }; 241 | 242 | Client.prototype.getOptions = function(patch) { 243 | var contentType, options; 244 | options = { 245 | headers: {} 246 | }; 247 | contentType = patch && this.apiVersion !== '1.0-preview.1' ? 'application/json-patch+json' : 'application/json'; 248 | options.headers['accept'] = 'application/json; api-version=' + this.apiVersion; 249 | options.headers['content-type'] = contentType; 250 | options.headers['user-agent'] = this.userAgent; 251 | return options; 252 | }; 253 | 254 | Client.prototype.getPatchContentType = function() { 255 | if (this.apiVersion === "1.0-preview.1") { 256 | return 'application/json'; 257 | } 258 | return 'application/json-patch+json'; 259 | }; 260 | 261 | Client.prototype.encodeFolderPath = function(folderParam) { 262 | if (!folderParam) { 263 | return ""; 264 | } 265 | return "/" + (folderParam.split("/").map(function(e) { 266 | return encodeURI(e); 267 | })).join("/"); 268 | }; 269 | 270 | Client.prototype.isGuid = function(id) { 271 | var guidPattern; 272 | guidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; 273 | return id.match(guidPattern); 274 | }; 275 | 276 | getVersion = function(version) { 277 | var dashPosition; 278 | dashPosition = version.indexOf("-"); 279 | if (dashPosition !== -1) { 280 | return version.substring(0, dashPosition); 281 | } 282 | return version; 283 | }; 284 | 285 | getMinorVersion = function(previewVersion) { 286 | var parts; 287 | parts = previewVersion.split("."); 288 | if (parts.length > 1) { 289 | return parts[1]; 290 | } 291 | return "0"; 292 | }; 293 | 294 | getVersionStage = function(version) { 295 | var dashPosition; 296 | dashPosition = version.indexOf("-"); 297 | if (dashPosition === -1) { 298 | return ""; 299 | } 300 | return version.substring(dashPosition); 301 | }; 302 | 303 | Client.prototype.requireMinimumVersion = function(version, requiredMinimumVersion) { 304 | var majorRequiredVersion, majorVersion, previewRequiredMinimumVersion, previewVersion; 305 | majorVersion = getVersion(version); 306 | majorRequiredVersion = getVersion(requiredMinimumVersion); 307 | if (majorVersion < majorRequiredVersion) { 308 | return false; 309 | } 310 | if (majorVersion > majorRequiredVersion) { 311 | return true; 312 | } 313 | previewVersion = getVersionStage(version); 314 | previewRequiredMinimumVersion = getVersionStage(requiredMinimumVersion); 315 | if ((previewVersion === previewRequiredMinimumVersion && previewRequiredMinimumVersion === "")) { 316 | return majorVersion >= majorRequiredVersion; 317 | } 318 | if (previewVersion !== "" && previewRequiredMinimumVersion !== "") { 319 | return (getMinorVersion(previewVersion)) >= (getMinorVersion(previewRequiredMinimumVersion)); 320 | } 321 | if (previewVersion === "" && previewRequiredMinimumVersion !== "") { 322 | return true; 323 | } 324 | return false; 325 | }; 326 | 327 | Client.prototype.checkAndRequireMinimumVersion = function(minimumVersion) { 328 | if (!this.requireMinimumVersion(this.apiVersion, minimumVersion)) { 329 | throw new Error("this method requires at least @{minimumVersion)"); 330 | } 331 | }; 332 | 333 | Client.prototype.getProjects = function(stateFilter, pageSize, skip, callback) { 334 | var path; 335 | if (typeof stateFilter === 'function') { 336 | callback = stateFilter; 337 | stateFilter = pageSize = skip = null; 338 | } else if (typeof pageSize === 'function') { 339 | callback = pageSize; 340 | pageSize = skip = null; 341 | } else if (typeof skip === 'function') { 342 | callback = skip; 343 | skip = null; 344 | } 345 | pageSize = pageSize != null ? pageSize : 100; 346 | skip = skip != null ? skip : 0; 347 | stateFilter = stateFilter != null ? stateFilter : 'WellFormed'; 348 | path = this.buildApiPath('projects', 'stateFilter=' + stateFilter + '&$top=' + pageSize + "&$skip=" + skip); 349 | return this.client.get(path, this.getOptions(), (function(_this) { 350 | return function(err, res, body) { 351 | return _this.parseReplyData(err, res, body, callback); 352 | }; 353 | })(this)); 354 | }; 355 | 356 | Client.prototype.getProject = function(projectId, includeCapabilities, callback) { 357 | var path; 358 | if (typeof includeCapabilities === 'function') { 359 | callback = includeCapabilities; 360 | includeCapabilities = false; 361 | } 362 | includeCapabilities = includeCapabilities != null ? includeCapabilities : false; 363 | path = this.buildApiPath('projects/' + projectId, 'includeCapabilities=' + includeCapabilities); 364 | return this.client.get(path, this.getOptions(), (function(_this) { 365 | return function(err, res, body) { 366 | return _this.parseReplyData(err, res, body, callback); 367 | }; 368 | })(this)); 369 | }; 370 | 371 | Client.prototype.getProjectCollections = function(pageSize, skip, callback) { 372 | var path; 373 | if (typeof pageSize === 'function') { 374 | callback = pageSize; 375 | pageSize = skip = null; 376 | } else if (typeof skip === 'function') { 377 | callback = skip; 378 | skip = null; 379 | } 380 | pageSize = pageSize != null ? pageSize : 100; 381 | skip = skip != null ? skip : 0; 382 | path = this.buildApiPath('projectcollections', '$top=' + pageSize + "&$skip=" + skip); 383 | return this.client.get(path, this.getOptions(), (function(_this) { 384 | return function(err, res, body) { 385 | return _this.parseReplyData(err, res, body, callback); 386 | }; 387 | })(this)); 388 | }; 389 | 390 | Client.prototype.getProjectCollection = function(collectionId, callback) { 391 | var path; 392 | path = this.buildApiPath('projectcollections/' + collectionId); 393 | return this.client.get(path, this.getOptions(), (function(_this) { 394 | return function(err, res, body) { 395 | return _this.parseReplyData(err, res, body, callback); 396 | }; 397 | })(this)); 398 | }; 399 | 400 | Client.prototype.getTeams = function(projectId, pageSize, skip, callback) { 401 | var path; 402 | if (typeof pageSize === 'function') { 403 | callback = pageSize; 404 | pageSize = skip = null; 405 | } else if (typeof skip === 'function') { 406 | callback = skip; 407 | skip = null; 408 | } 409 | pageSize = pageSize != null ? pageSize : 100; 410 | skip = skip != null ? skip : 0; 411 | path = this.buildApiPath('projects/' + projectId + '/teams', '$top=' + pageSize + '&$skip=' + skip); 412 | return this.client.get(path, this.getOptions(), (function(_this) { 413 | return function(err, res, body) { 414 | return _this.parseReplyData(err, res, body, callback); 415 | }; 416 | })(this)); 417 | }; 418 | 419 | Client.prototype.getTeam = function(projectId, teamId, callback) { 420 | var path; 421 | path = this.buildApiPath('projects/' + projectId + '/teams/' + teamId); 422 | return this.client.get(path, this.getOptions(), (function(_this) { 423 | return function(err, res, body) { 424 | return _this.parseReplyData(err, res, body, callback); 425 | }; 426 | })(this)); 427 | }; 428 | 429 | Client.prototype.getTeamMembers = function(projectId, teamId, pageSize, skip, callback) { 430 | var path; 431 | if (typeof pageSize === 'function') { 432 | callback = pageSize; 433 | pageSize = skip = null; 434 | } else if (typeof skip === 'function') { 435 | callback = skip; 436 | skip = null; 437 | } 438 | pageSize = pageSize != null ? pageSize : 100; 439 | skip = skip != null ? skip : 0; 440 | path = this.buildApiPath('projects/' + projectId + '/teams/' + teamId + '/members', '$top=' + pageSize + '&$skip=' + skip); 441 | return this.client.get(path, this.getOptions(), (function(_this) { 442 | return function(err, res, body) { 443 | return _this.parseReplyData(err, res, body, callback); 444 | }; 445 | })(this)); 446 | }; 447 | 448 | Client.prototype.getTags = function(scope, includeInactive, callback) { 449 | var path; 450 | if (typeof includeInactive === 'function') { 451 | callback = includeInactive; 452 | includeInactive = false; 453 | } 454 | path = this.buildApiPath('tagging/scopes/' + scope + '/tags', 'includeinactive=' + includeInactive); 455 | return this.client.get(path, this.getOptions(), (function(_this) { 456 | return function(err, res, body) { 457 | return _this.parseReplyData(err, res, body, callback); 458 | }; 459 | })(this)); 460 | }; 461 | 462 | Client.prototype.getTag = function(scope, tag, callback) { 463 | var path, tagId; 464 | tagId = encodeURI(tag); 465 | path = this.buildApiPath('tagging/scopes/' + scope + '/tags/' + tagId); 466 | return this.client.get(path, this.getOptions(), (function(_this) { 467 | return function(err, res, body) { 468 | return _this.parseReplyData(err, res, body, callback); 469 | }; 470 | })(this)); 471 | }; 472 | 473 | Client.prototype.createTag = function(scope, name, callback) { 474 | var path, tag; 475 | tag = { 476 | name: name 477 | }; 478 | path = this.buildApiPath('tagging/scopes/' + scope + '/tags'); 479 | return this.client.post(path, tag, this.getOptions(), (function(_this) { 480 | return function(err, res, body) { 481 | return _this.parseReplyData(err, res, body, callback); 482 | }; 483 | })(this)); 484 | }; 485 | 486 | Client.prototype.updateTag = function(scope, tagId, name, active, callback) { 487 | var path, tag; 488 | tag = { 489 | name: name, 490 | active: active 491 | }; 492 | path = this.buildApiPath('tagging/scopes/' + scope + '/tags/' + tagId); 493 | return this.client.patch(path, tag, this.getOptions(), (function(_this) { 494 | return function(err, res, body) { 495 | return _this.parseReplyData(err, res, body, callback); 496 | }; 497 | })(this)); 498 | }; 499 | 500 | Client.prototype.deleteTag = function(scope, tag, callback) { 501 | var path, tagId; 502 | tagId = encodeURI(tag); 503 | path = this.buildApiPath('tagging/scopes/' + scope + '/tags/' + tagId); 504 | return this.client.del(path, this.getOptions(), (function(_this) { 505 | return function(err, res, body) { 506 | return _this.parseReplyData(err, res, body, callback); 507 | }; 508 | })(this)); 509 | }; 510 | 511 | Client.prototype.getWorkItemIds = function(wiql, projectName, callback) { 512 | var params, path, query; 513 | if (typeof projectName === 'function') { 514 | callback = projectName; 515 | projectName = null; 516 | } 517 | params = null; 518 | if (this.apiVersion === "1.0-preview.1") { 519 | query = { 520 | wiql: wiql 521 | }; 522 | if (projectName) { 523 | projectName = encodeURI(projectName); 524 | params = '@project=' + projectName; 525 | } 526 | path = this.buildApiPath('wit/queryresults', params); 527 | } else { 528 | query = { 529 | query: wiql 530 | }; 531 | if (projectName) { 532 | path = this.buildApiPath('wit/wiql', params, { 533 | projectName: projectName 534 | }); 535 | } else { 536 | path = this.buildApiPath('wit/wiql', params); 537 | } 538 | } 539 | return this.client.post(path, query, this.getOptions(), (function(_this) { 540 | return function(err, res, body) { 541 | return _this.parseReplyData(err, res, body, function(err, results) { 542 | var ids; 543 | if (err) { 544 | return callback(err, results); 545 | } else { 546 | if (results && results.results) { 547 | ids = _.map(results.results, 'sourceId'); 548 | } else { 549 | ids = _.map(results.workItems, 'id'); 550 | } 551 | return callback(err, ids); 552 | } 553 | }); 554 | }; 555 | })(this)); 556 | }; 557 | 558 | Client.prototype.getWorkItemIdsByQuery = function(queryId, projectName, callback) { 559 | var params, path, query; 560 | if (typeof projectName === 'function') { 561 | callback = projectName; 562 | projectName = null; 563 | } 564 | query = { 565 | id: queryId 566 | }; 567 | params = null; 568 | if (projectName) { 569 | projectName = encodeURI(projectName); 570 | params = '@project=' + projectName; 571 | } 572 | path = this.buildApiPath('wit/queryresults', params); 573 | return this.client.post(path, query, this.getOptions(), (function(_this) { 574 | return function(err, res, body) { 575 | return _this.parseReplyData(err, res, body, function(err, results) { 576 | var ids; 577 | if (err) { 578 | return callback(err, results); 579 | } else { 580 | ids = _.map(results.results, 'sourceId'); 581 | return callback(err, ids); 582 | } 583 | }); 584 | }; 585 | })(this)); 586 | }; 587 | 588 | Client.prototype.getWorkItemsById = function(ids, fields, asOf, expand, callback) { 589 | var params, path; 590 | if (typeof fields === 'function') { 591 | callback = fields; 592 | fields = asOf = expand = null; 593 | } else if (typeof asOf === 'function') { 594 | callback = asOf; 595 | asOf = expand = null; 596 | } else if (typeof expand === 'function') { 597 | callback = expand; 598 | expand = null; 599 | } 600 | if (typeof ids === 'Array') { 601 | ids = ids.join(','); 602 | } 603 | if (typeof fields === 'Array') { 604 | fields = fields.join(','); 605 | } 606 | params = 'ids=' + ids; 607 | if (fields) { 608 | params += '&fields=' + fields; 609 | } 610 | if (asOf) { 611 | params += '&asof=' + asOf; 612 | } 613 | if (expand) { 614 | params += '&$expand=' + expand; 615 | } 616 | path = this.buildApiPath('wit/workitems', params); 617 | return this.client.get(path, this.getOptions(), (function(_this) { 618 | return function(err, res, body) { 619 | return _this.parseReplyData(err, res, body, callback); 620 | }; 621 | })(this)); 622 | }; 623 | 624 | Client.prototype.getWorkItem = function(id, expand, callback) { 625 | var params, path; 626 | if (typeof expand === 'function') { 627 | callback = expand; 628 | expand = null; 629 | } 630 | params = null; 631 | if (expand) { 632 | params = '$expand=' + expand; 633 | } 634 | path = this.buildApiPath('wit/workitems/' + id, params); 635 | return this.client.get(path, this.getOptions(), (function(_this) { 636 | return function(err, res, body) { 637 | return _this.parseReplyData(err, res, body, callback); 638 | }; 639 | })(this)); 640 | }; 641 | 642 | Client.prototype.createWorkItem = function(item, projectName, workItemType, callback) { 643 | var path; 644 | if (this.apiVersion === "1.0-preview.1") { 645 | if (callback == null) { 646 | callback = projectName; 647 | } 648 | path = this.buildApiPath('wit/workitems'); 649 | return this.client.post(path, item, this.getOptions(), (function(_this) { 650 | return function(err, res, body) { 651 | var _ref; 652 | if (err) { 653 | return callback(err, body); 654 | } else if (res.statusCode === 400) { 655 | return callback(((_ref = body.exception) != null ? _ref.Message : void 0) || "Error Creating work item", body); 656 | } else { 657 | return _this.parseReplyData(err, res, body, callback); 658 | } 659 | }; 660 | })(this)); 661 | } else { 662 | path = this.buildApiPath("wit/workitems/$" + workItemType, null, { 663 | projectName: projectName 664 | }); 665 | return this.client.patch(path, item, this.getOptions(true), (function(_this) { 666 | return function(err, res, body) { 667 | if (err) { 668 | return callback(err, body); 669 | } else if (res.statusCode === 404) { 670 | return callback((body != null ? body.message : void 0) || "Error Creating work item", body); 671 | } else { 672 | return _this.parseReplyData(err, res, body, callback); 673 | } 674 | }; 675 | })(this)); 676 | } 677 | }; 678 | 679 | Client.prototype.updateWorkItem = function(id, operations, callback) { 680 | var path; 681 | path = this.buildApiPath('wit/workitems/' + id); 682 | return this.client.patch(path, operations, this.getOptions(true), (function(_this) { 683 | return function(err, res, body) { 684 | if ((res != null ? res.statusCode : void 0) === 404) { 685 | callback((body != null ? body.message : void 0) || "Error Creating work item", body); 686 | } else { 687 | 688 | } 689 | return _this.parseReplyData(err, res, body, callback); 690 | }; 691 | })(this)); 692 | }; 693 | 694 | Client.prototype.updateWorkItems = function(items, callback) { 695 | var path; 696 | path = this.buildApiPath('wit/workitems'); 697 | return this.client.patch(path, items, this.getOptions(true), (function(_this) { 698 | return function(err, res, body) { 699 | if ((res != null ? res.statusCode : void 0) === 404) { 700 | callback((body != null ? body.message : void 0) || "Error Creating work item", body); 701 | } else { 702 | 703 | } 704 | return _this.parseReplyData(err, res, body, callback); 705 | }; 706 | })(this)); 707 | }; 708 | 709 | Client.prototype.getWorkItemUpdates = function(id, pageSize, skip, callback) { 710 | var path; 711 | if (typeof pageSize === 'function') { 712 | callback = pageSize; 713 | pageSize = skip = null; 714 | } else if (typeof skip === 'function') { 715 | callback = skip; 716 | skip = null; 717 | } 718 | pageSize = pageSize != null ? pageSize : 100; 719 | skip = skip != null ? skip : 0; 720 | path = this.buildApiPath('wit/workitems/' + id + '/updates', '$top=' + pageSize + '&$skip=' + skip); 721 | return this.client.get(path, this.getOptions(), (function(_this) { 722 | return function(err, res, body) { 723 | return _this.parseReplyData(err, res, body, callback); 724 | }; 725 | })(this)); 726 | }; 727 | 728 | Client.prototype.getWorkItemUpdate = function(id, rev, callback) { 729 | var path; 730 | path = this.buildApiPath('wit/workitems/' + id + '/updates/' + rev); 731 | return this.client.get(path, this.getOptions(), (function(_this) { 732 | return function(err, res, body) { 733 | return _this.parseReplyData(err, res, body, callback); 734 | }; 735 | })(this)); 736 | }; 737 | 738 | Client.prototype.getWorkItemRevision = function(id, rev, callback) { 739 | var path; 740 | path = this.buildApiPath('wit/workitems/' + id + '/revisions/' + rev); 741 | return this.client.get(path, this.getOptions(), (function(_this) { 742 | return function(err, res, body) { 743 | return _this.parseReplyData(err, res, body, callback); 744 | }; 745 | })(this)); 746 | }; 747 | 748 | Client.prototype.uploadAttachment = function(project, areaPath, fileName, file, callback) { 749 | var params, path; 750 | params = 'project=' + encodeURI(project); 751 | params += '&area=' + encodeURI(areaPath); 752 | params += '&filename=' + encodeURI(fileName); 753 | path = this.buildApiPath('wit/attachments', params); 754 | return this.client.post(path, file, this.getOptions(), (function(_this) { 755 | return function(err, res, body) { 756 | return _this.parseReplyData(err, res, body, callback); 757 | }; 758 | })(this)); 759 | }; 760 | 761 | Client.prototype.addAttachmentToWorkItem = function(id, rev, fileName, locationId, comment, callback) { 762 | var item, path; 763 | item = { 764 | id: id, 765 | rev: rev, 766 | resourceLinks: [ 767 | { 768 | type: 'attachment', 769 | name: fileName, 770 | location: locationId, 771 | comment: comment 772 | } 773 | ] 774 | }; 775 | path = this.buildApiPath('wit/workitems/' + id); 776 | return this.client.patch(path, item, this.getOptions(true), (function(_this) { 777 | return function(err, res, body) { 778 | return _this.parseReplyData(err, res, body, callback); 779 | }; 780 | })(this)); 781 | }; 782 | 783 | Client.prototype.getQueries = function(projectName, depth, expand, folderPath, includeDeleted, callback) { 784 | var folderPathParam, params, path; 785 | if (typeof depth === 'function') { 786 | callback = depth; 787 | depth = expand = folderPath = null; 788 | includeDeleted = false; 789 | } else if (typeof expand === 'function') { 790 | callback = expand; 791 | expand = folderPath = null; 792 | includeDeleted = false; 793 | } else if (typeof folderPath === 'function') { 794 | callback = folderPath; 795 | folderPath = null; 796 | includeDeleted = false; 797 | } else if (typeof includeDeleted === 'function') { 798 | callback = includeDeleted; 799 | includeDeleted = false; 800 | } 801 | folderPathParam = ""; 802 | if (this.apiVersion === '1.0-preview.1') { 803 | params = '&project=' + projectName; 804 | } else { 805 | folderPathParam = this.encodeFolderPath(folderPath); 806 | } 807 | if (depth) { 808 | params += '&$depth=' + depth; 809 | } 810 | if (expand) { 811 | params += '&$expand=' + expand; 812 | } 813 | if (this.apiVersion === '1.0-preview.1') { 814 | path = this.buildApiPath('wit/queries', params); 815 | } else { 816 | if (includeDeleted) { 817 | params = '&$includeDeleted=' + includeDeleted; 818 | } 819 | } 820 | path = this.buildApiPath('wit/queries' + folderPathParam, params, { 821 | projectName: projectName 822 | }); 823 | return this.client.get(path, this.getOptions(), (function(_this) { 824 | return function(err, res, body) { 825 | return _this.parseReplyData(err, res, body, callback); 826 | }; 827 | })(this)); 828 | }; 829 | 830 | Client.prototype.getQuery = function(projectName, queryOrFolderId, folderPath, callback) { 831 | var folderPathParam, path; 832 | if (typeof folderPath === 'function') { 833 | callback = folderPath; 834 | folderPath = null; 835 | } 836 | if (this.apiVersion === '1.0-preview.1') { 837 | path = this.buildApiPath('wit/queries/' + queryOrFolderId); 838 | } else { 839 | folderPathParam = this.encodeFolderPath(folderPath); 840 | path = this.buildApiPath('wit/queries' + folderPathParam + '/' + queryOrFolderId, null, { 841 | projectName: projectName 842 | }); 843 | } 844 | return this.client.get(path, this.getOptions(), (function(_this) { 845 | return function(err, res, body) { 846 | return _this.parseReplyData(err, res, body, callback); 847 | }; 848 | })(this)); 849 | }; 850 | 851 | Client.prototype.createQuery = function(projectName, name, folderIdOrPath, wiql, callback) { 852 | var path, query; 853 | if (this.apiVersion === '1.0-preview.1') { 854 | query = { 855 | name: name, 856 | parentId: folderIdOrPath, 857 | wiql: wiql 858 | }; 859 | path = this.buildApiPath('wit/queries'); 860 | } else { 861 | path = this.buildApiPath('wit/queries' + (this.encodeFolderPath(folderIdOrPath)), null, { 862 | projectName: projectName 863 | }); 864 | query = { 865 | name: name, 866 | wiql: wiql 867 | }; 868 | } 869 | return this.client.post(path, query, this.getOptions(), (function(_this) { 870 | return function(err, res, body) { 871 | return _this.parseReplyData(err, res, body, callback); 872 | }; 873 | })(this)); 874 | }; 875 | 876 | Client.prototype.updateQuery = function(projectName, queryIdOrName, name, folderIdOrPath, wiql, callback) { 877 | var path, query; 878 | if (this.apiVersion === '1.0-preview.1') { 879 | path = this.buildApiPath('wit/queries/' + queryId); 880 | query = { 881 | id: queryIdOrName, 882 | name: name, 883 | parentId: folderIdOrPath, 884 | wiql: wiql 885 | }; 886 | path = this.buildApiPath('wit/queries'); 887 | } else { 888 | path = this.isGuid(queryIdOrName) ? this.buildApiPath('wit/queries/' + queryIdOrName, null, { 889 | projectName: projectName 890 | }) : this.buildApiPath('wit/queries' + (this.encodeFolderPath(folderIdOrPath)) + '/' + queryIdOrName, null, { 891 | projectName: projectName 892 | }); 893 | query = { 894 | name: name, 895 | wiql: wiql 896 | }; 897 | } 898 | return this.client.patch(path, query, this.getOptions(), (function(_this) { 899 | return function(err, res, body) { 900 | return _this.parseReplyData(err, res, body, callback); 901 | }; 902 | })(this)); 903 | }; 904 | 905 | Client.prototype.createFolder = function(projectName, name, parentFolderIdOrPath, callback) { 906 | var folder, path; 907 | if (this.apiVersion === '1.0-preview.1') { 908 | folder = { 909 | name: name, 910 | parentId: parentFolderIdOrPath, 911 | type: "folder" 912 | }; 913 | path = this.buildApiPath('wit/queries'); 914 | } else { 915 | path = this.buildApiPath('wit/queries' + (this.encodeFolderPath(parentFolderIdOrPath)), null, { 916 | projectName: projectName 917 | }); 918 | folder = { 919 | name: name, 920 | isFolder: "true" 921 | }; 922 | } 923 | return this.client.post(path, folder, this.getOptions(), (function(_this) { 924 | return function(err, res, body) { 925 | return _this.parseReplyData(err, res, body, callback); 926 | }; 927 | })(this)); 928 | }; 929 | 930 | Client.prototype.deleteQuery = function(projectName, queryIdOrPath, callback) { 931 | var path; 932 | if (this.apiVersion === '1.0-preview.1') { 933 | path = this.buildApiPath('wit/queries/' + queryIdOrPath); 934 | } else { 935 | path = this.buildApiPath('wit/queries' + (this.encodeFolderPath(queryIdOrPath)), null, { 936 | projectName: projectName 937 | }); 938 | } 939 | return this.client.del(path, this.getOptions(), (function(_this) { 940 | return function(err, res, body) { 941 | return _this.parseReplyData(err, res, body, callback); 942 | }; 943 | })(this)); 944 | }; 945 | 946 | Client.prototype.deleteFolder = function(projectName, queryIdOrPath, callback) { 947 | return this.deleteQuery(projectName, queryIdOrPath, callback); 948 | }; 949 | 950 | Client.prototype.getWorkItemTypes = function(projectName, callback) { 951 | var path; 952 | this.checkAndRequireMinimumVersion("1.0-preview.2"); 953 | path = this.buildApiPath('wit/workitemtypes', null, { 954 | projectName: projectName 955 | }); 956 | return this.client.get(path, this.getOptions(), (function(_this) { 957 | return function(err, res, body) { 958 | return _this.parseReplyData(err, res, body, callback); 959 | }; 960 | })(this)); 961 | }; 962 | 963 | Client.prototype.getWorkItemType = function(projectName, workItemType, callback) { 964 | var path; 965 | this.checkAndRequireMinimumVersion("1.0-preview.2"); 966 | path = this.buildApiPath('wit/workitemtypes/' + workItemType, null, { 967 | projectName: projectName 968 | }); 969 | return this.client.get(path, this.getOptions(), (function(_this) { 970 | return function(err, res, body) { 971 | return _this.parseReplyData(err, res, body, callback); 972 | }; 973 | })(this)); 974 | }; 975 | 976 | Client.prototype.getWorkItemRelationTypes = function(callback) { 977 | var path; 978 | this.checkAndRequireMinimumVersion("1.0-preview.2"); 979 | path = this.buildApiPath('wit/workitemrelationtypes'); 980 | return this.client.get(path, this.getOptions(), (function(_this) { 981 | return function(err, res, body) { 982 | return _this.parseReplyData(err, res, body, callback); 983 | }; 984 | })(this)); 985 | }; 986 | 987 | Client.prototype.getWorkItemRelationType = function(relationName, callback) { 988 | var path; 989 | this.checkAndRequireMinimumVersion("1.0-preview.2"); 990 | path = this.buildApiPath('wit/workitemrelationtypes/' + relationName); 991 | return this.client.get(path, this.getOptions(), (function(_this) { 992 | return function(err, res, body) { 993 | return _this.parseReplyData(err, res, body, callback); 994 | }; 995 | })(this)); 996 | }; 997 | 998 | Client.prototype.getWorkItemCategories = function(projectName, callback) { 999 | var path; 1000 | this.checkAndRequireMinimumVersion("1.0-preview.2"); 1001 | path = this.buildApiPath('wit/workitemtypecategories', null, { 1002 | projectName: projectName 1003 | }); 1004 | return this.client.get(path, this.getOptions(), (function(_this) { 1005 | return function(err, res, body) { 1006 | return _this.parseReplyData(err, res, body, callback); 1007 | }; 1008 | })(this)); 1009 | }; 1010 | 1011 | Client.prototype.getWorkItemCategory = function(projectName, categoryName, callback) { 1012 | var path; 1013 | this.checkAndRequireMinimumVersion("1.0-preview.2"); 1014 | path = this.buildApiPath('wit/workitemtypecategories/' + categoryName, null, { 1015 | projectName: projectName 1016 | }); 1017 | return this.client.get(path, this.getOptions(), (function(_this) { 1018 | return function(err, res, body) { 1019 | return _this.parseReplyData(err, res, body, callback); 1020 | }; 1021 | })(this)); 1022 | }; 1023 | 1024 | Client.prototype.getWorkItemFields = function(callback) { 1025 | var path; 1026 | this.checkAndRequireMinimumVersion("1.0-preview.2"); 1027 | path = this.buildApiPath('wit/fields'); 1028 | return this.client.get(path, this.getOptions(), (function(_this) { 1029 | return function(err, res, body) { 1030 | return _this.parseReplyData(err, res, body, callback); 1031 | }; 1032 | })(this)); 1033 | }; 1034 | 1035 | Client.prototype.getWorkItemField = function(referenceName, callback) { 1036 | var path; 1037 | this.checkAndRequireMinimumVersion("1.0-preview.2"); 1038 | path = this.buildApiPath('wit/fields/' + referenceName); 1039 | return this.client.get(path, this.getOptions(), (function(_this) { 1040 | return function(err, res, body) { 1041 | return _this.parseReplyData(err, res, body, callback); 1042 | }; 1043 | })(this)); 1044 | }; 1045 | 1046 | Client.prototype.getAccounts = function(memberId, callback) { 1047 | var path; 1048 | this.checkAndRequireOAuth('getAccounts'); 1049 | path = this.buildApiPath('accounts', 'memberid=' + memberId, { 1050 | excludeCollection: true 1051 | }); 1052 | return this.clientSPS.get(path, this.getOptions(), (function(_this) { 1053 | return function(err, res, body) { 1054 | return _this.parseReplyData(err, res, body, callback); 1055 | }; 1056 | })(this)); 1057 | }; 1058 | 1059 | Client.prototype.getCurrentProfile = function(callback) { 1060 | var path; 1061 | this.checkAndRequireOAuth("getCurrentProfile"); 1062 | path = this.buildApiPath('profile/profiles/me', null, { 1063 | excludeCollection: true 1064 | }); 1065 | return this.clientSPS.get(path, this.getOptions(), (function(_this) { 1066 | return function(err, res, body) { 1067 | return _this.parseReplyData(err, res, body, callback); 1068 | }; 1069 | })(this)); 1070 | }; 1071 | 1072 | Client.prototype.getConnectionData = function(callback) { 1073 | var path; 1074 | path = this.buildApiPath('connectionData'); 1075 | return this.client.get(path, this.getOptions(), (function(_this) { 1076 | return function(err, res, body) { 1077 | return _this.parseReplyData(err, res, body, callback); 1078 | }; 1079 | })(this)); 1080 | }; 1081 | 1082 | Client.prototype.getRooms = function(callback) { 1083 | var path; 1084 | path = this.buildApiPath('chat/rooms'); 1085 | return this.client.get(path, this.getOptions(), (function(_this) { 1086 | return function(err, res, body) { 1087 | return _this.parseReplyData(err, res, body, callback); 1088 | }; 1089 | })(this)); 1090 | }; 1091 | 1092 | Client.prototype.getRoom = function(roomId, callback) { 1093 | var path; 1094 | path = this.buildApiPath('chat/rooms/' + roomId); 1095 | return this.client.get(path, this.getOptions(), (function(_this) { 1096 | return function(err, res, body) { 1097 | return _this.parseReplyData(err, res, body, callback); 1098 | }; 1099 | })(this)); 1100 | }; 1101 | 1102 | Client.prototype.createRoom = function(name, description, callback) { 1103 | var path, room; 1104 | path = this.buildApiPath('chat/rooms'); 1105 | room = { 1106 | name: name, 1107 | description: description 1108 | }; 1109 | return this.client.post(path, room, this.getOptions(), (function(_this) { 1110 | return function(err, res, body) { 1111 | return _this.parseReplyData(err, res, body, callback); 1112 | }; 1113 | })(this)); 1114 | }; 1115 | 1116 | Client.prototype.updateRoom = function(roomId, name, description, callback) { 1117 | var path, room; 1118 | path = this.buildApiPath('chat/rooms/' + roomId); 1119 | room = { 1120 | name: name, 1121 | description: description 1122 | }; 1123 | return this.client.patch(path, room, this.getOptions(), (function(_this) { 1124 | return function(err, res, body) { 1125 | return _this.parseReplyData(err, res, body, callback); 1126 | }; 1127 | })(this)); 1128 | }; 1129 | 1130 | Client.prototype.deleteRoom = function(roomId, callback) { 1131 | var path; 1132 | path = this.buildApiPath('chat/rooms/' + roomId); 1133 | return this.client.del(path, this.getOptions(), (function(_this) { 1134 | return function(err, res, body) { 1135 | return _this.parseReplyData(err, res, body, callback); 1136 | }; 1137 | })(this)); 1138 | }; 1139 | 1140 | Client.prototype.getRoomUsers = function(roomId, callback) { 1141 | var path; 1142 | path = this.buildApiPath('chat/rooms/' + roomId + '/users'); 1143 | return this.client.get(path, this.getOptions(), (function(_this) { 1144 | return function(err, res, body) { 1145 | return _this.parseReplyData(err, res, body, callback); 1146 | }; 1147 | })(this)); 1148 | }; 1149 | 1150 | Client.prototype.getRoomUser = function(roomId, userId, callback) { 1151 | var path; 1152 | path = this.buildApiPath('chat/rooms/' + roomId + '/users/' + userId); 1153 | return this.client.get(path, this.getOptions(), (function(_this) { 1154 | return function(err, res, body) { 1155 | return _this.parseReplyData(err, res, body, callback); 1156 | }; 1157 | })(this)); 1158 | }; 1159 | 1160 | Client.prototype.joinRoom = function(roomId, userId, userGuid, callback) { 1161 | var path; 1162 | path = this.buildApiPath('chat/rooms/' + roomId + '/users/' + userGuid); 1163 | return this.client.put(path, userId, this.getOptions(), function(err, res, body) { 1164 | return callback(err, res.statusCode); 1165 | }); 1166 | }; 1167 | 1168 | Client.prototype.leaveRoom = function(roomId, userId, callback) { 1169 | var path; 1170 | path = this.buildApiPath('chat/rooms/' + roomId + '/users/' + userId); 1171 | return this.client.del(path, this.getOptions(), (function(_this) { 1172 | return function(err, res, body) { 1173 | return _this.parseReplyData(err, res, body, callback); 1174 | }; 1175 | })(this)); 1176 | }; 1177 | 1178 | Client.prototype.getMessages = function(roomId, startDate, endDate, callback) { 1179 | var params, path; 1180 | params = null; 1181 | if (typeof startDate === 'function') { 1182 | callback = startDate; 1183 | } else if (startDate || endDate) { 1184 | params = '$filter='; 1185 | if (typeof endDate === 'function') { 1186 | callback = endDate; 1187 | endDate = null; 1188 | } 1189 | if (startDate && endDate) { 1190 | params += 'postedtime ge ' + startDate + ' and postedtime lt ' + endDate; 1191 | } else if (startDate) { 1192 | params += 'postedtime ge ' + startDate; 1193 | } else { 1194 | params += 'postedtime lt ' + endDate; 1195 | } 1196 | } 1197 | path = this.buildApiPath('chat/rooms/' + roomId + '/messages', params); 1198 | return this.client.get(path, this.getOptions(), (function(_this) { 1199 | return function(err, res, body) { 1200 | return _this.parseReplyData(err, res, body, callback); 1201 | }; 1202 | })(this)); 1203 | }; 1204 | 1205 | Client.prototype.getMessage = function(roomId, messageId, callback) { 1206 | var path; 1207 | path = this.buildApiPath('chat/rooms/' + roomId + '/messages/' + messageId); 1208 | return this.client.get(path, this.getOptions(), (function(_this) { 1209 | return function(err, res, body) { 1210 | return _this.parseReplyData(err, res, body, callback); 1211 | }; 1212 | })(this)); 1213 | }; 1214 | 1215 | Client.prototype.createMessage = function(roomId, message, callback) { 1216 | var path; 1217 | path = this.buildApiPath('chat/rooms/' + roomId + '/messages'); 1218 | return this.client.post(path, message, this.getOptions(), (function(_this) { 1219 | return function(err, res, body) { 1220 | return _this.parseReplyData(err, res, body, callback); 1221 | }; 1222 | })(this)); 1223 | }; 1224 | 1225 | Client.prototype.updateMessage = function(roomId, messageId, message, callback) { 1226 | var path; 1227 | path = this.buildApiPath('chat/rooms/' + roomId + '/messages/' + messageId); 1228 | return this.client.patch(path, null, this.getOptions(), (function(_this) { 1229 | return function(err, res, body) { 1230 | return _this.parseReplyData(err, res, body, callback); 1231 | }; 1232 | })(this)); 1233 | }; 1234 | 1235 | Client.prototype.deleteMessage = function(roomId, messageId, callback) { 1236 | var path; 1237 | path = this.buildApiPath('chat/rooms/' + roomId + '/messages/' + messageId); 1238 | return this.client.del(path, this.getOptions(), (function(_this) { 1239 | return function(err, res, body) { 1240 | return _this.parseReplyData(err, res, body, callback); 1241 | }; 1242 | })(this)); 1243 | }; 1244 | 1245 | Client.prototype.getRootBranches = function(includeChildren, includeDeleted, callback) { 1246 | var p, params, path; 1247 | if (typeof includeChildren === 'function') { 1248 | callback = includeChildren; 1249 | includeChildren = includeDeleted = false; 1250 | } 1251 | if (typeof includeDeleted === 'function') { 1252 | callback = includeDeleted; 1253 | includeDeleted = false; 1254 | } 1255 | params = []; 1256 | if (includeChildren) { 1257 | params.push("includechildren=true"); 1258 | } 1259 | if (includeDeleted) { 1260 | params.push("includedeleted=true"); 1261 | } 1262 | p = params.join('&'); 1263 | path = this.buildApiPath('tfvc/branches', p); 1264 | return this.client.get(path, this.getOptions(), (function(_this) { 1265 | return function(err, res, body) { 1266 | return _this.parseReplyData(err, res, body, callback); 1267 | }; 1268 | })(this)); 1269 | }; 1270 | 1271 | Client.prototype.getBranch = function(path, includeChildren, includeParent, includeDeleted, callback) { 1272 | var p, params; 1273 | if (typeof includeChildren === 'function') { 1274 | callback = includeChildren; 1275 | includeChildren = includeParent = includeDeleted = false; 1276 | } 1277 | if (typeof includeParent === 'function') { 1278 | callback = includeParent; 1279 | includeParent = includeDeleted = false; 1280 | } 1281 | if (typeof includeDeleted === 'function') { 1282 | callback = includeDeleted; 1283 | includeDeleted = false; 1284 | } 1285 | params = []; 1286 | if (includeChildren) { 1287 | params.push("includechildren=true"); 1288 | } 1289 | if (includeParent) { 1290 | params.push("includeparent=true"); 1291 | } 1292 | if (includeDeleted) { 1293 | params.push("includedeleted=true"); 1294 | } 1295 | p = params.join('&'); 1296 | path = this.buildApiPath('tfvc/branches/' + path, p); 1297 | return this.client.get(path, this.getOptions(), (function(_this) { 1298 | return function(err, res, body) { 1299 | return _this.parseReplyData(err, res, body, callback); 1300 | }; 1301 | })(this)); 1302 | }; 1303 | 1304 | Client.prototype.getShelveSets = function(owner, maxCommentLength, pageSize, skip, callback) { 1305 | var p, params, path; 1306 | if (typeof owner === 'function') { 1307 | callback = owner; 1308 | owner = maxCommentLength = pageSize = skip = null; 1309 | } 1310 | if (typeof maxCommentLength === 'function') { 1311 | callback = maxCommentLength; 1312 | maxCommentLength = pageSize = skip = null; 1313 | } 1314 | if (typeof pageSize === 'function') { 1315 | callback = pageSize; 1316 | pageSize = skip = null; 1317 | } 1318 | if (typeof skip === 'function') { 1319 | callback = skip; 1320 | skip = null; 1321 | } 1322 | maxCommentLength = maxCommentLength != null ? maxCommentLength : 80; 1323 | pageSize = pageSize != null ? pageSize : 100; 1324 | skip = skip != null ? skip : 0; 1325 | params = []; 1326 | if (owner) { 1327 | params.push('owner=' + owner); 1328 | } 1329 | params.push('maxcommentlength=' + maxCommentLength); 1330 | params.push('$top=' + pageSize); 1331 | params.push('$skip=' + skip); 1332 | p = params.join('&'); 1333 | path = this.buildApiPath('tfvc/shelvesets', p); 1334 | return this.client.get(path, this.getOptions(), (function(_this) { 1335 | return function(err, res, body) { 1336 | return _this.parseReplyData(err, res, body, callback); 1337 | }; 1338 | })(this)); 1339 | }; 1340 | 1341 | Client.prototype.getChangeSets = function(queryOptions, callback) { 1342 | var p, params, path; 1343 | if (typeof queryOptions === 'function') { 1344 | callback = queryOptions; 1345 | queryOptions = null; 1346 | } 1347 | params = []; 1348 | if (queryOptions) { 1349 | if (queryOptions.itemPath) { 1350 | params.push('itempath=' + queryOptions.itemPath); 1351 | } 1352 | if (queryOptions.version) { 1353 | params.push('version=' + queryOptions.version); 1354 | } 1355 | if (queryOptions.versionType) { 1356 | params.push('versiontype=' + queryOptions.versionType); 1357 | } 1358 | if (queryOptions.versionOption) { 1359 | params.push('versionoption=' + queryOptions.versionOption); 1360 | } 1361 | if (queryOptions.author) { 1362 | params.push('author=' + queryOptions.author); 1363 | } 1364 | if (queryOptions.fromId) { 1365 | params.push('fromId=' + queryOptions.fromId); 1366 | } 1367 | if (queryOptions.toId) { 1368 | params.push('toId=' + queryOptions.toId); 1369 | } 1370 | if (queryOptions.fromDate) { 1371 | params.push('fromDate=' + queryOptions.fromDate); 1372 | } 1373 | if (queryOptions.toDate) { 1374 | params.push('toDate=' + queryOptions.toDate); 1375 | } 1376 | if (queryOptions.pageSize) { 1377 | params.push('$top=' + queryOptions.pageSize); 1378 | } 1379 | if (queryOptions.skip) { 1380 | params.push('$skip=' + queryOptions.skip); 1381 | } 1382 | if (queryOptions.orderby) { 1383 | params.push('$orderby=' + queryOptions.orderby); 1384 | } 1385 | if (queryOptions.maxCommentLength) { 1386 | params.push('maxcommentlength=' + queryOptions.maxCommentLength); 1387 | } 1388 | } 1389 | p = params.join('&'); 1390 | path = this.buildApiPath('tfvc/changesets', p); 1391 | return this.client.get(path, this.getOptions(), (function(_this) { 1392 | return function(err, res, body) { 1393 | return _this.parseReplyData(err, res, body, callback); 1394 | }; 1395 | })(this)); 1396 | }; 1397 | 1398 | Client.prototype.getChangeSet = function(changesetId, queryOptions, callback) { 1399 | var p, params, path; 1400 | if (typeof queryOptions === 'function') { 1401 | callback = queryOptions; 1402 | queryOptions = null; 1403 | } 1404 | params = []; 1405 | if (queryOptions) { 1406 | if (queryOptions.includeDetails) { 1407 | params.push('includedetails=true'); 1408 | } 1409 | if (queryOptions.includeWorkItems) { 1410 | params.push('includeworkitems=true'); 1411 | } 1412 | if (queryOptions.maxChangeCount) { 1413 | params.push('maxchangecount=' + queryOptions.maxChangeCount); 1414 | } 1415 | if (queryOptions.maxCommentLength) { 1416 | params.push('maxcommentlength=' + queryOptions.maxCommentLength); 1417 | } 1418 | } 1419 | p = params.join('&'); 1420 | path = this.buildApiPath('tfvc/changesets/' + changesetId, p); 1421 | return this.client.get(path, this.getOptions(), (function(_this) { 1422 | return function(err, res, body) { 1423 | return _this.parseReplyData(err, res, body, callback); 1424 | }; 1425 | })(this)); 1426 | }; 1427 | 1428 | Client.prototype.getChangeSetChanges = function(queryOptions, callback) { 1429 | var p, params, path, url; 1430 | if (typeof queryOptions === 'function') { 1431 | callback = queryOptions; 1432 | queryOptions = null; 1433 | } 1434 | url = 'tfvc/changesets/latest/changes'; 1435 | params = []; 1436 | if (queryOptions) { 1437 | if (queryOptions.id) { 1438 | url = 'tfvc/changesets/' + queryOptions.id + '/changes'; 1439 | } 1440 | if (queryOptions.pageSize) { 1441 | params.push('$top=' + queryOptions.pageSize); 1442 | } 1443 | if (queryOptions.skip) { 1444 | params.push('$skip=' + queryOptions.skip); 1445 | } 1446 | } 1447 | p = params.join('&'); 1448 | path = this.buildApiPath(url, p); 1449 | return this.client.get(path, this.getOptions(), (function(_this) { 1450 | return function(err, res, body) { 1451 | return _this.parseReplyData(err, res, body, callback); 1452 | }; 1453 | })(this)); 1454 | }; 1455 | 1456 | Client.prototype.getChangeSetWorkItems = function(queryOptions, callback) { 1457 | var p, params, path, url; 1458 | if (typeof queryOptions === 'function') { 1459 | callback = queryOptions; 1460 | queryOptions = null; 1461 | } 1462 | url = 'tfvc/changesets/latest/workitems'; 1463 | params = []; 1464 | if (queryOptions) { 1465 | if (queryOptions.id) { 1466 | url = 'tfvc/changesets/' + queryOptions.id + '/workitems'; 1467 | } 1468 | if (queryOptions.pageSize) { 1469 | params.push('$top=' + queryOptions.pageSize); 1470 | } 1471 | if (queryOptions.skip) { 1472 | params.push('$skip=' + queryOptions.skip); 1473 | } 1474 | } 1475 | p = params.join('&'); 1476 | path = this.buildApiPath(url, p); 1477 | return this.client.get(path, this.getOptions(), (function(_this) { 1478 | return function(err, res, body) { 1479 | return _this.parseReplyData(err, res, body, callback); 1480 | }; 1481 | })(this)); 1482 | }; 1483 | 1484 | Client.prototype.getLabels = function(queryOptions, callback) { 1485 | var p, params, path; 1486 | if (typeof queryOptions === 'function') { 1487 | callback = queryOptions; 1488 | queryOptions = null; 1489 | } 1490 | params = []; 1491 | if (queryOptions) { 1492 | if (queryOptions.name) { 1493 | params.push('name=' + queryOptions.name); 1494 | } 1495 | if (queryOptions.owner) { 1496 | params.push('owner=' + queryOptions.owner); 1497 | } 1498 | if (queryOptions.itemLabelFilter) { 1499 | params.push('itemlabelfilter=' + queryOptions.itemLabelFilter); 1500 | } 1501 | if (queryOptions.pageSize) { 1502 | params.push('$top=' + queryOptions.pageSize); 1503 | } 1504 | if (queryOptions.skip) { 1505 | params.push('$skip=' + queryOptions.skip); 1506 | } 1507 | } 1508 | p = params.join('&'); 1509 | path = this.buildApiPath('tfvc/labels', p); 1510 | return this.client.get(path, this.getOptions(), (function(_this) { 1511 | return function(err, res, body) { 1512 | return _this.parseReplyData(err, res, body, callback); 1513 | }; 1514 | })(this)); 1515 | }; 1516 | 1517 | Client.prototype.getLabel = function(labelId, maxItemCount, callback) { 1518 | var params, path, _ref; 1519 | if (typeof maxItemCount === 'function') { 1520 | callback = maxItemCount; 1521 | maxItemCount = null; 1522 | } 1523 | params = (_ref = 'maxitemcount=' + maxItemCount) != null ? _ref : ''; 1524 | path = this.buildApiPath('tfvc/labels/' + labelId, params); 1525 | return this.client.get(path, this.getOptions(), (function(_this) { 1526 | return function(err, res, body) { 1527 | return _this.parseReplyData(err, res, body, callback); 1528 | }; 1529 | })(this)); 1530 | }; 1531 | 1532 | Client.prototype.getItemsByLabel = function(labelId, pageSize, skip, callback) { 1533 | var params, path; 1534 | if (typeof pageSize === 'function') { 1535 | callback = pageSize; 1536 | pageSize = skip = null; 1537 | } 1538 | if (typeof skip === 'function') { 1539 | callback = skip; 1540 | skip = null; 1541 | } 1542 | pageSize = pageSize != null ? pageSize : 100; 1543 | skip = skip != null ? skip : 0; 1544 | params = '$top=' + pageSize + '&$skip=' + skip; 1545 | path = this.buildApiPath('tfvc/labels/' + labelId + '/items', params); 1546 | return this.client.get(path, this.getOptions(), (function(_this) { 1547 | return function(err, res, body) { 1548 | return _this.parseReplyData(err, res, body, callback); 1549 | }; 1550 | })(this)); 1551 | }; 1552 | 1553 | Client.prototype.getRepositories = function(projectId, callback) { 1554 | var path; 1555 | path = ''; 1556 | if (typeof projectId === 'function') { 1557 | callback = projectId; 1558 | projectId = null; 1559 | } 1560 | if (projectId) { 1561 | path = this.buildApiPath('git/' + projectId + '/repositories'); 1562 | } else { 1563 | path = this.buildApiPath('git/repositories'); 1564 | } 1565 | return this.client.get(path, this.getOptions(), (function(_this) { 1566 | return function(err, res, body) { 1567 | return _this.parseReplyData(err, res, body, callback); 1568 | }; 1569 | })(this)); 1570 | }; 1571 | 1572 | Client.prototype.getRepository = function(repositoryIdOrName, projectId, callback) { 1573 | var path, repo; 1574 | path = ''; 1575 | if (typeof projectId === 'function') { 1576 | callback = projectId; 1577 | projectId = null; 1578 | } 1579 | repo = encodeURI(repositoryIdOrName); 1580 | if (projectId) { 1581 | path = this.buildApiPath('git/' + projectId + '/repositories/' + repo); 1582 | } else { 1583 | path = this.buildApiPath('git/repositories/' + repo); 1584 | } 1585 | return this.client.get(path, this.getOptions(), (function(_this) { 1586 | return function(err, res, body) { 1587 | return _this.parseReplyData(err, res, body, callback); 1588 | }; 1589 | })(this)); 1590 | }; 1591 | 1592 | Client.prototype.createRepository = function(projectId, name, callback) { 1593 | var path, repo; 1594 | repo = { 1595 | name: name, 1596 | project: { 1597 | id: projectId 1598 | } 1599 | }; 1600 | path = this.buildApiPath('git/repositories'); 1601 | return this.client.post(path, repo, this.getOptions(), (function(_this) { 1602 | return function(err, res, body) { 1603 | return _this.parseReplyData(err, res, body, callback); 1604 | }; 1605 | })(this)); 1606 | }; 1607 | 1608 | Client.prototype.renameRepository = function(repositoryId, name, callback) { 1609 | var path, repo; 1610 | repo = { 1611 | id: repositoryId, 1612 | name: name 1613 | }; 1614 | path = this.buildApiPath('git/repositories/' + repositoryId); 1615 | return this.client.patch(path, repo, this.getOptions(), (function(_this) { 1616 | return function(err, res, body) { 1617 | return _this.parseReplyData(err, res, body, callback); 1618 | }; 1619 | })(this)); 1620 | }; 1621 | 1622 | Client.prototype.deleteRepository = function(repositoryId, callback) { 1623 | var path; 1624 | path = this.buildApiPath('git/repositories/' + repositoryId); 1625 | return this.client.del(path, this.getOptions(), (function(_this) { 1626 | return function(err, res, body) { 1627 | return _this.parseReplyData(err, res, body, callback); 1628 | }; 1629 | })(this)); 1630 | }; 1631 | 1632 | Client.prototype.getCommits = function(repositoryId, itemPath, committer, author, fromDate, toDate, pageSize, skip, callback) { 1633 | var params, path; 1634 | if (typeof itemPath === 'function') { 1635 | callback = itemPath; 1636 | itemPath = committer = author = fromDate = toDate = pageSize = skip = null; 1637 | } else if (typeof committer === 'function') { 1638 | callback = committer; 1639 | committer = author = fromDate = toDate = pageSize = skip = null; 1640 | } else if (typeof author === 'function') { 1641 | callback = author; 1642 | author = fromDate = toDate = pageSize = skip = null; 1643 | } else if (typeof fromDate === 'function') { 1644 | callback = fromDate; 1645 | fromDate = toDate = pageSize = skip = null; 1646 | } else if (typeof toDate === 'function') { 1647 | callback = toDate; 1648 | toDate = pageSize = skip = null; 1649 | } else if (typeof pageSize === 'function') { 1650 | callback = pageSize; 1651 | pageSize = skip = null; 1652 | } else if (typeof skip === 'function') { 1653 | callback = skip; 1654 | skip = null; 1655 | } 1656 | skip = skip != null ? skip : 0; 1657 | pageSize = pageSize != null ? pageSize : 1000; 1658 | params = '$top=' + pageSize + '&$skip=' + skip; 1659 | if (itemPath) { 1660 | params += '&itempath=' + itemPath; 1661 | } 1662 | if (committer) { 1663 | params += '&committer=' + committer; 1664 | } 1665 | if (author) { 1666 | params += '&author=' + author; 1667 | } 1668 | if (fromDate) { 1669 | params += '&fromdate=' + fromDate; 1670 | } 1671 | if (toDate) { 1672 | params += '&todate=' + toDate; 1673 | } 1674 | path = this.buildApiPath('git/repositories/' + repositoryId + '/commits', params); 1675 | return this.client.get(path, this.getOptions(), (function(_this) { 1676 | return function(err, res, body) { 1677 | return _this.parseReplyData(err, res, body, callback); 1678 | }; 1679 | })(this)); 1680 | }; 1681 | 1682 | Client.prototype.getCommit = function(repositoryId, commitId, changeCount, callback) { 1683 | var path; 1684 | if (typeof changeCount === 'function') { 1685 | callback = changeCount; 1686 | changeCount = 0; 1687 | } 1688 | path = this.buildApiPath('git/repositories/' + repositoryId + '/commits/' + commitId, 'changeCount=' + changeCount); 1689 | return this.client.get(path, this.getOptions(), (function(_this) { 1690 | return function(err, res, body) { 1691 | return _this.parseReplyData(err, res, body, callback); 1692 | }; 1693 | })(this)); 1694 | }; 1695 | 1696 | Client.prototype.getDiffs = function(repositoryId, baseVersionType, baseVersion, targetVersionType, targetVersion, pageSize, skip, callback) { 1697 | var params, path; 1698 | if (typeof baseVersionType === 'function') { 1699 | callback = baseVersionType; 1700 | baseVersionType = baseVersion = targetVersionType = targetVersion = pageSize = skip = null; 1701 | } else if (typeof baseVersion === 'function') { 1702 | callback = baseVersion; 1703 | baseVersion = targetVersionType = targetVersion = pageSize = skip = null; 1704 | } else if (typeof targetVersionType === 'function') { 1705 | callback = targetVersionType; 1706 | targetVersionType = targetVersion = pageSize = skip = null; 1707 | } else if (typeof targetVersion === 'function') { 1708 | callback = targetVersion; 1709 | targetVersion = pageSize = skip = null; 1710 | } else if (typeof pageSize === 'function') { 1711 | callback = pageSize; 1712 | pageSize = skip = null; 1713 | } else if (typeof skip === 'function') { 1714 | callback = skip; 1715 | skip = null; 1716 | } 1717 | skip = skip != null ? skip : 0; 1718 | pageSize = pageSize != null ? pageSize : 1000; 1719 | params = '$top=' + pageSize + '&$skip=' + skip; 1720 | if (baseVersionType) { 1721 | params += '&baseversiontype=' + baseVersionType; 1722 | } 1723 | if (targetVersionType) { 1724 | params += '&targetversiontype=' + targetVersionType; 1725 | } 1726 | if (baseVersion) { 1727 | params += '&baseversion=' + baseVersion; 1728 | } 1729 | if (targetVersion) { 1730 | params += '&targetversion=' + targetVersion; 1731 | } 1732 | path = this.buildApiPath('git/repositories/' + repositoryId + '/diffs/commits', params); 1733 | return this.client.get(path, this.getOptions(), (function(_this) { 1734 | return function(err, res, body) { 1735 | return _this.parseReplyData(err, res, body, callback); 1736 | }; 1737 | })(this)); 1738 | }; 1739 | 1740 | Client.prototype.getPushes = function(repositoryId, fromDate, toDate, pusherId, pageSize, skip, callback) { 1741 | var params, path; 1742 | if (typeof fromDate === 'function') { 1743 | callback = fromDate; 1744 | fromDate = toDate = pusherId = pageSize = skip = null; 1745 | } else if (typeof toDate === 'function') { 1746 | callback = toDate; 1747 | toDate = pusherId = pageSize = skip = null; 1748 | } else if (typeof pusherId === 'function') { 1749 | callback = pusherId; 1750 | pusherId = pageSize = skip = null; 1751 | } else if (typeof pageSize === 'function') { 1752 | callback = pageSize; 1753 | pageSize = skip = null; 1754 | } else if (typeof skip === 'function') { 1755 | callback = skip; 1756 | skip = null; 1757 | } 1758 | skip = skip != null ? skip : 0; 1759 | pageSize = pageSize != null ? pageSize : 1000; 1760 | params = '$top=' + pageSize + '&$skip=' + skip; 1761 | if (fromDate) { 1762 | params += '&fromdate=' + fromDate; 1763 | } 1764 | if (toDate) { 1765 | params += '&todate=' + toDate; 1766 | } 1767 | if (pusherId) { 1768 | params += '&pusherid=' + pusherId; 1769 | } 1770 | path = this.buildApiPath('git/repositories/' + repositoryId + '/pushes', params); 1771 | return this.client.get(path, this.getOptions(), (function(_this) { 1772 | return function(err, res, body) { 1773 | return _this.parseReplyData(err, res, body, callback); 1774 | }; 1775 | })(this)); 1776 | }; 1777 | 1778 | Client.prototype.getStats = function(repositoryId, branchName, baseVersionType, baseVersion, callback) { 1779 | var params, path, url; 1780 | if (typeof branchName === 'function') { 1781 | callback = branchName; 1782 | branchName = baseVersionType = baseVersion = null; 1783 | } else if (typeof baseVersionType === 'function') { 1784 | callback = baseVersionType; 1785 | baseVersionType = baseVersion = null; 1786 | } else if (typeof baseVersion === 'function') { 1787 | callback = baseVersion; 1788 | baseVersion = null; 1789 | } 1790 | params = []; 1791 | if (baseVersionType) { 1792 | params.push('baseversiontype=' + baseVersionType); 1793 | } 1794 | if (baseVersion) { 1795 | params.push('baseversion=' + baseVersion); 1796 | } 1797 | url = 'git/repositories/' + repositoryId + '/stats/branches'; 1798 | if (branchName) { 1799 | url += '/' + branchName; 1800 | } 1801 | path = this.buildApiPath(url, params.join('&')); 1802 | return this.client.get(path, this.getOptions(), (function(_this) { 1803 | return function(err, res, body) { 1804 | return _this.parseReplyData(err, res, body, callback); 1805 | }; 1806 | })(this)); 1807 | }; 1808 | 1809 | Client.prototype.getRefs = function(repositoryId, filter, callback) { 1810 | var path, url; 1811 | if (typeof filter === 'function') { 1812 | callback = filter; 1813 | filter = null; 1814 | } 1815 | url = 'git/repositories/' + repositoryId + '/refs'; 1816 | if (filter) { 1817 | url += '/' + filter; 1818 | } 1819 | path = this.buildApiPath(url); 1820 | return this.client.get(path, this.getOptions(), (function(_this) { 1821 | return function(err, res, body) { 1822 | return _this.parseReplyData(err, res, body, callback); 1823 | }; 1824 | })(this)); 1825 | }; 1826 | 1827 | Client.prototype.getBuildDefinitions = function(callback) { 1828 | var path; 1829 | path = this.buildApiPath('build/definitions'); 1830 | return this.client.get(path, this.getOptions(), (function(_this) { 1831 | return function(err, res, body) { 1832 | return _this.parseReplyData(err, res, body, callback); 1833 | }; 1834 | })(this)); 1835 | }; 1836 | 1837 | Client.prototype.queueBuild = function(buildRequest, callback) { 1838 | var path; 1839 | path = this.buildApiPath('build/requests'); 1840 | return this.client.post(path, buildRequest, this.getOptions(), (function(_this) { 1841 | return function(err, res, body) { 1842 | return _this.parseReplyData(err, res, body, callback); 1843 | }; 1844 | })(this)); 1845 | }; 1846 | 1847 | Client.prototype.getPublishers = function(callback) { 1848 | var path; 1849 | path = this.buildApiPath('hooks/publishers'); 1850 | return this.client.get(path, this.getOptions(), (function(_this) { 1851 | return function(err, res, body) { 1852 | return _this.parseReplyData(err, res, body, callback); 1853 | }; 1854 | })(this)); 1855 | }; 1856 | 1857 | Client.prototype.getConsumers = function(callback) { 1858 | var path; 1859 | path = this.buildApiPath('hooks/consumers'); 1860 | return this.client.get(path, this.getOptions(), (function(_this) { 1861 | return function(err, res, body) { 1862 | return _this.parseReplyData(err, res, body, callback); 1863 | }; 1864 | })(this)); 1865 | }; 1866 | 1867 | Client.prototype.getConsumer = function(consumerId, callback) { 1868 | var path; 1869 | path = this.buildApiPath('hooks/consumers/' + consumerId); 1870 | return this.client.get(path, this.getOptions(), (function(_this) { 1871 | return function(err, res, body) { 1872 | return _this.parseReplyData(err, res, body, callback); 1873 | }; 1874 | })(this)); 1875 | }; 1876 | 1877 | Client.prototype.getConsumerActions = function(consumerId, callback) { 1878 | var path; 1879 | path = this.buildApiPath('hooks/consumers/' + consumerId + '/actions'); 1880 | return this.client.get(path, this.getOptions(), (function(_this) { 1881 | return function(err, res, body) { 1882 | return _this.parseReplyData(err, res, body, callback); 1883 | }; 1884 | })(this)); 1885 | }; 1886 | 1887 | Client.prototype.getConsumerAction = function(consumerId, action, callback) { 1888 | var path; 1889 | path = this.buildApiPath('hooks/consumers/' + consumerId + '/actions/' + action); 1890 | return this.client.get(path, this.getOptions(), (function(_this) { 1891 | return function(err, res, body) { 1892 | return _this.parseReplyData(err, res, body, callback); 1893 | }; 1894 | })(this)); 1895 | }; 1896 | 1897 | Client.prototype.getSubscriptions = function(callback) { 1898 | var path; 1899 | path = this.buildApiPath('hooks/subscriptions'); 1900 | return this.client.get(path, this.getOptions(), (function(_this) { 1901 | return function(err, res, body) { 1902 | return _this.parseReplyData(err, res, body, callback); 1903 | }; 1904 | })(this)); 1905 | }; 1906 | 1907 | Client.prototype.createSubscription = function(subscription, callback) { 1908 | var path; 1909 | path = this.buildApiPath('hooks/subscriptions'); 1910 | return this.client.post(path, subscription, this.getOptions(), (function(_this) { 1911 | return function(err, res, body) { 1912 | return _this.parseReplyData(err, res, body, callback); 1913 | }; 1914 | })(this)); 1915 | }; 1916 | 1917 | Client.prototype.querySubscriptions = function(queryOptions, callback) { 1918 | var path; 1919 | path = this.buildApiPath('hooks/subscriptionsquery'); 1920 | return this.client.post(path, queryOptions, this.getOptions(), (function(_this) { 1921 | return function(err, res, body) { 1922 | return _this.parseReplyData(err, res, body, callback); 1923 | }; 1924 | })(this)); 1925 | }; 1926 | 1927 | Client.prototype.deleteSubscription = function(id, callback) { 1928 | var path; 1929 | path = this.buildApiPath('hooks/subscriptions/' + id); 1930 | return this.client.del(path, this.getOptions(), (function(_this) { 1931 | return function(err, res, body) { 1932 | return _this.parseReplyData(err, res, body, callback); 1933 | }; 1934 | })(this)); 1935 | }; 1936 | 1937 | return Client; 1938 | 1939 | })(); 1940 | -------------------------------------------------------------------------------- /spec/vso-client.spec.js: -------------------------------------------------------------------------------- 1 | // The integration test require the existence of a Team Projet named "TFS INTEGRATION" 2 | // Or you can override that name using the VSO_TEST_PROJECT env name 3 | // Agile process template and git 4 | // It should have at least one bug assigned to the user that is executing the tests 5 | // It should at least have a task (the first task requires at least 2 revisions) 6 | var mocha = require( 'mocha' ); 7 | var should = require( 'should' ); 8 | var _ = require( 'lodash' ); 9 | var async = require( 'async' ); 10 | var Client = require( '../public/vso-client' ); 11 | var url = process.env.VSO_URL || 'https://your-account.visualstudio.com/'; 12 | var collection = process.env.VSO_COLLECTION || 'DefaultCollection'; 13 | var username = process.env.VSO_USER || 'your-username'; 14 | var password = process.env.VSO_PWD || 'your-password'; 15 | var serviceAccountUser = process.env.VSO_SERVICE_ACCOUNT_USER || 'your service account username'; 16 | var serviceAccountPassword = process.env.VSO_SERVICE_ACCOUNT_PWD || 'your service account password'; 17 | var oauthToken = process.env.VSO_OAUTH_TOKEN || 'dummyAccessToken'; 18 | var memberId = process.env.VSO_MEMBER_ID || '00000000-0000-0000-0000-000000000000'; 19 | var proxy = process.env.VSO_PROXY; 20 | var testProjectName = process.env.VSO_TEST_PROJECT || 'TFS INTEGRATION'; 21 | 22 | function getOptions( overrideVersion ) { 23 | options = { 24 | apiVersion: overrideVersion || "1.0" 25 | }; 26 | 27 | if ( typeof (proxy) !== "undefined" || proxy !== null ) { 28 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 29 | options.clientOptions = { 30 | proxy: proxy 31 | }; 32 | } 33 | 34 | return options; 35 | } 36 | 37 | describe( 'Versioning tests', function() { 38 | 39 | before( function( done ) { 40 | client = Client.createClient( url, collection, username, password, getOptions() ); 41 | done(); 42 | } ); 43 | 44 | it( 'equal version minimum met', function() { 45 | var minimumVersionMet = client.requireMinimumVersion( "1.0", "1.0" ); 46 | 47 | minimumVersionMet.should.equal( true ); 48 | } ); 49 | 50 | it( 'equal version preview minimum met', function() { 51 | var minimumVersionMet = client.requireMinimumVersion( "1.0-preview", "1.0-preview" ); 52 | 53 | minimumVersionMet.should.equal( true ); 54 | } ); 55 | 56 | it( 'greater version minimum met', function() { 57 | var minimumVersionMet = client.requireMinimumVersion( "1.1", "1.0" ); 58 | 59 | minimumVersionMet.should.equal( true ); 60 | } ); 61 | 62 | it( 'greater version with preview minimum met', function() { 63 | var minimumVersionMet = client.requireMinimumVersion( "1.1-preview", "1.0-preview" ); 64 | 65 | minimumVersionMet.should.equal( true ); 66 | } ); 67 | 68 | it( 'greater version with preview version minimum met', function() { 69 | var minimumVersionMet = client.requireMinimumVersion( "1.1-preview", "1.0" ); 70 | 71 | minimumVersionMet.should.equal( true ); 72 | } ); 73 | 74 | it( 'greater version with preview version minimum not met', function() { 75 | var minimumVersionMet = client.requireMinimumVersion( "1.0-preview", "1.1" ); 76 | 77 | minimumVersionMet.should.equal( false ); 78 | } ); 79 | 80 | it( 'equal version greater preview minimum met', function() { 81 | var minimumVersionMet = client.requireMinimumVersion( "1.0-preview.2", "1.0-preview" ); 82 | 83 | minimumVersionMet.should.equal( true ); 84 | } ); 85 | 86 | it( 'equal version lower preview minimum not met', function() { 87 | var minimumVersionMet = client.requireMinimumVersion( "1.0-preview", "1.0-preview.2" ); 88 | 89 | minimumVersionMet.should.equal( false ); 90 | } ); 91 | 92 | it( 'equal version lower non preview minimum met met', function() { 93 | var minimumVersionMet = client.requireMinimumVersion( "1.0", "1.0-preview.2" ); 94 | 95 | minimumVersionMet.should.equal( true ); 96 | } ); 97 | 98 | it( 'equal version lower running preview required non preview minimum non met', function() { 99 | var minimumVersionMet = client.requireMinimumVersion( "1.0-preview", "1.0" ); 100 | 101 | minimumVersionMet.should.equal( false ); 102 | } ); 103 | 104 | it( 'lower version minimum not met', function() { 105 | var minimumVersionMet = client.requireMinimumVersion( "1.0", "1.2" ); 106 | 107 | minimumVersionMet.should.equal( false ); 108 | } ); 109 | 110 | it( 'lower version minimum preview not met', function() { 111 | var minimumVersionMet = client.requireMinimumVersion( "1.0", "1.2-preview" ); 112 | 113 | minimumVersionMet.should.equal( false ); 114 | } ); 115 | 116 | 117 | } ); 118 | 119 | describe( 'User Agent Tests', function() { 120 | 121 | it( 'has a default user agent string', function() { 122 | var clientNoOptions = Client.createClient( url, collection, username, password ); 123 | should.exist( clientNoOptions ); 124 | clientNoOptions.should.have.property('userAgent'); 125 | clientNoOptions.userAgent.should.startWith('vso-client/1.0'); 126 | // console.log(clientNoOptions.userAgent); 127 | } ); 128 | 129 | it( 'has a custom user agent string', function() { 130 | var appName = 'My App 1.0'; 131 | var clientWithOptions = Client.createClient( url, collection, username, password, { userAgent: appName } ); 132 | should.exist( clientWithOptions ); 133 | clientWithOptions.should.have.property('userAgent'); 134 | clientWithOptions.userAgent.should.startWith(appName); 135 | // console.log(clientWithOptions.userAgent); 136 | } ); 137 | 138 | } ); 139 | 140 | describe( 'VSO API 1.0 Tests', function() { 141 | this.timeout( 20000 ); 142 | var client = {}; 143 | 144 | before( function( done ) { 145 | client = Client.createClient( url, collection, username, password, getOptions( "1.0" ) ); 146 | clientOAuth = Client.createOAuthClient( url, collection, oauthToken, getOptions( "1.0" ) ); 147 | // console.log(oauthToken); 148 | done(); 149 | } ); 150 | 151 | describe( 'client tests', function() { 152 | 153 | it( 'has a credential valid client', function() { 154 | should.exist( client ); 155 | client.should.have.property( 'url' ); 156 | client.should.have.property( '_authType' ); 157 | client._authType.should.equal( 'Credential' ); 158 | should.exist( client.client ); 159 | should.not.exist( client.clientSPS ); 160 | } ); 161 | 162 | it( 'has a OAuth valid client', function() { 163 | should.exist( clientOAuth ); 164 | clientOAuth.should.have.property( 'url' ); 165 | clientOAuth.should.have.property( '_authType' ); 166 | clientOAuth._authType.should.equal( 'OAuth' ); 167 | should.exist( clientOAuth.client ); 168 | should.exist( clientOAuth.clientSPS ); 169 | } ); 170 | 171 | it( 'has overriden version', function() { 172 | var version = "1.0-preview-unitTest"; 173 | var clientWithVersion = Client.createClient( url, collection, username, password, { apiVersion: version } ); 174 | 175 | clientWithVersion.apiVersion.should.equal( version ); 176 | } ); 177 | 178 | it( 'has default version', function() { 179 | var expectedVersion = "1.0"; 180 | var clientWithVersion = Client.createClient( url, collection, username, password ); 181 | 182 | clientWithVersion.apiVersion.should.equal( expectedVersion ); 183 | } ); 184 | 185 | describe( 'auth tests', function() { 186 | 187 | it( 'fails authentication with error', function( done ) { 188 | 189 | var clientWithWrongCredential = Client.createClient( url, collection, "DUMMY_USER_NAME", "DUMMY PASSWORD SET TO FAIL", getOptions() ); 190 | 191 | clientWithWrongCredential.getProjects( function( err, projects ) { 192 | should.exist( err ); 193 | done(); 194 | } ); 195 | } ); 196 | 197 | } ); 198 | 199 | } ); 200 | 201 | describe( 'project tests', function() { 202 | var testProject = null; 203 | var testCollection = null; 204 | var testTeam = null; 205 | var tagName = null; 206 | var testTag = null; 207 | 208 | before( function( done ) { 209 | tagName = 'testTag-' + ( Math.random() + 1 ).toString( 36 ).substring( 7 ); 210 | done(); 211 | } ); 212 | 213 | // --------------------------------------- 214 | // Projects 215 | // --------------------------------------- 216 | 217 | it( 'returns a list of projects', function( done ) { 218 | client.getProjects( function( err, projects ) { 219 | should.not.exist( err ); 220 | should.exist( projects ); 221 | // console.log(projects); 222 | projects.length.should.be.above( 0 ); 223 | var project = _.find( projects, function( p ) { 224 | return p.name === testProjectName; 225 | } ); 226 | should.exist( project ); 227 | // console.log(project); 228 | project.should.have.property( 'id' ); 229 | project.should.have.property( 'name' ); 230 | project.should.have.property( 'url' ); 231 | project.should.have.property( 'state' ); 232 | testProject = project; 233 | done(); 234 | } ); 235 | } ); 236 | 237 | it( 'returns only one project', function( done ) { 238 | client.getProjects( null, 1, 0, function( err, projects ) { 239 | should.not.exist( err ); 240 | should.exist( projects ); 241 | // console.log(projects); 242 | projects.length.should.equal( 1 ); 243 | var project = projects[ 0 ]; 244 | project.should.have.property( 'id' ); 245 | project.should.have.property( 'name' ); 246 | project.should.have.property( 'url' ); 247 | done(); 248 | } ); 249 | } ); 250 | 251 | it( 'retrieves a project by id', function( done ) { 252 | should.exist( testProject ); 253 | client.getProject( testProject.id, false, function( err, project ) { 254 | if ( err ) { 255 | console.log( err ); 256 | console.log( project ); 257 | } 258 | should.not.exist( err ); 259 | should.exist( project ); 260 | // console.log(project); 261 | project.should.have.property( 'id' ); 262 | project.should.have.property( 'name' ); 263 | project.should.have.property( 'url' ); 264 | should.exist( project._links ); 265 | should.exist( project._links.collection ); 266 | should.exist( project.defaultTeam ); 267 | done(); 268 | } ); 269 | } ); 270 | 271 | it( 'retrieves a project by id with capabilities', function( done ) { 272 | should.exist( testProject ); 273 | client.getProject( testProject.id, true, function( err, project ) { 274 | if ( err ) { 275 | console.log( err ); 276 | console.log( project ); 277 | } 278 | should.not.exist( err ); 279 | should.exist( project ); 280 | // console.log(project); 281 | project.should.have.property( 'id' ); 282 | project.should.have.property( 'name' ); 283 | project.should.have.property( 'url' ); 284 | should.exist( project._links ); 285 | should.exist( project._links.collection ); 286 | should.exist( project.capabilities ); 287 | should.exist( project.capabilities.versioncontrol ); 288 | should.exist( project.capabilities.processTemplate ); 289 | should.exist( project.defaultTeam ); 290 | done(); 291 | } ); 292 | } ); 293 | 294 | // --------------------------------------- 295 | // Project Teams 296 | // --------------------------------------- 297 | 298 | it( 'retrieves teams by project id', function( done ) { 299 | should.exist( testProject ); 300 | client.getTeams( testProject.id, function( err, teams ) { 301 | if ( err ) { 302 | console.log( err ); 303 | console.log( teams ); 304 | } 305 | should.not.exist( err ); 306 | should.exist( teams ); 307 | // console.log(teams); 308 | teams.length.should.be.above( 0 ); 309 | var team = teams[ 0 ]; 310 | team.should.have.property( 'id' ); 311 | team.should.have.property( 'name' ); 312 | team.should.have.property( 'url' ); 313 | team.should.have.property( 'description' ); 314 | team.should.have.property( 'identityUrl' ); 315 | testTeam = team; 316 | done(); 317 | } ); 318 | } ); 319 | 320 | it( 'retrieves a team by project and team id', function( done ) { 321 | should.exist( testProject ); 322 | should.exist( testTeam ); 323 | client.getTeam( testProject.id, testTeam.id, function( err, team ) { 324 | if ( err ) { 325 | console.log( err ); 326 | console.log( team ); 327 | } 328 | should.not.exist( err ); 329 | should.exist( team ); 330 | team.should.have.property( 'id' ); 331 | team.should.have.property( 'name' ); 332 | team.should.have.property( 'url' ); 333 | team.should.have.property( 'description' ); 334 | team.should.have.property( 'identityUrl' ); 335 | done(); 336 | } ); 337 | } ); 338 | 339 | it( 'retrieves team members by project and team id', function( done ) { 340 | should.exist( testProject ); 341 | should.exist( testTeam ); 342 | client.getTeamMembers( testProject.id, testTeam.id, function( err, members ) { 343 | if ( err ) { 344 | console.log( err ); 345 | console.log( members ); 346 | } 347 | should.not.exist( err ); 348 | should.exist( members ); 349 | // console.log(members); 350 | members.length.should.be.above( 0 ); 351 | var member = members[ 0 ]; 352 | member.should.have.property( 'id' ); 353 | member.should.have.property( 'displayName' ); 354 | member.should.have.property( 'uniqueName' ); 355 | member.should.have.property( 'url' ); 356 | member.should.have.property( 'imageUrl' ); 357 | done(); 358 | } ); 359 | } ); 360 | 361 | // --------------------------------------- 362 | // Collections 363 | // --------------------------------------- 364 | 365 | it( 'returns a list of project collections', function( done ) { 366 | client.getProjectCollections( function( err, collections ) { 367 | should.not.exist( err ); 368 | should.exist( collections ); 369 | // console.log(collections); 370 | collections.length.should.be.above( 0 ); 371 | var collection = collections[ 0 ]; 372 | collection.should.have.property( 'id' ); 373 | collection.should.have.property( 'name' ); 374 | collection.should.have.property( 'url' ); 375 | collection.should.have.property( 'collectionUrl' ); 376 | collection.should.have.property( 'state' ); 377 | testCollection = collection; 378 | done(); 379 | } ); 380 | } ); 381 | 382 | it( 'returns a project collection by id', function( done ) { 383 | should.exist( testCollection ); 384 | client.getProjectCollection( testCollection.id, function( err, collection ) { 385 | should.not.exist( err ); 386 | should.exist( collection ); 387 | collection.should.have.property( 'id' ); 388 | collection.should.have.property( 'name' ); 389 | collection.should.have.property( 'url' ); 390 | collection.should.have.property( 'collectionUrl' ); 391 | collection.should.have.property( 'state' ); 392 | done(); 393 | } ); 394 | } ); 395 | 396 | // --------------------------------------- 397 | // Tags 398 | // --------------------------------------- 399 | 400 | it( 'creates a tag', function( done ) { 401 | should.exist( testProject ); 402 | client.createTag( testProject.id, tagName, function( err, tag ) { 403 | if ( err ) { 404 | console.log( err ); 405 | console.log( tag ); 406 | } 407 | should.not.exist( err ); 408 | should.exist( tag ); 409 | // console.log(tag); 410 | testTag = tag; 411 | done(); 412 | } ); 413 | } ); 414 | 415 | it( 'updates a tag', function( done ) { 416 | should.exist( testProject ); 417 | should.exist( testTag ); 418 | client.updateTag( testProject.id, testTag.id, tagName + '-updated', true, function( err, tag ) { 419 | if ( err ) { 420 | console.log( err ); 421 | console.log( tag ); 422 | } 423 | should.not.exist( err ); 424 | should.exist( tag ); 425 | // console.log(tag); 426 | done(); 427 | } ); 428 | } ); 429 | 430 | it( 'retrieves tags by project id', function( done ) { 431 | should.exist( testProject ); 432 | // Work-around, tags don't always immediately appear after being created 433 | setTimeout( function( done ) { 434 | client.getTags( testProject.id, true, function( err, tags ) { 435 | if ( err ) { 436 | console.log( err ); 437 | console.log( tags ); 438 | } 439 | should.not.exist( err ); 440 | should.exist( tags ); 441 | // console.log(tags); 442 | tags.length.should.be.above( 0 ); 443 | if ( tags.length > 0 ) { 444 | var tag = tags[ 0 ]; 445 | tag.should.have.property( 'id' ); 446 | tag.should.have.property( 'name' ); 447 | tag.should.have.property( 'active' ); 448 | tag.should.have.property( 'url' ); 449 | } 450 | done(); 451 | } ); 452 | }, 5000, done ); 453 | } ); 454 | 455 | it( 'deletes a tag', function( done ) { 456 | should.exist( testProject ); 457 | should.exist( testTag ); 458 | client.deleteTag( testProject.id, testTag.id, function( err, tag ) { 459 | if ( err ) { 460 | console.log( err ); 461 | console.log( tag ); 462 | } 463 | should.not.exist( err ); 464 | should.exist( tag ); 465 | // console.log(tag); 466 | done(); 467 | } ); 468 | } ); 469 | 470 | } ); 471 | 472 | // --------------------------------------- 473 | // Queries 474 | // --------------------------------------- 475 | 476 | describe( 'queries', function() { 477 | 478 | var myQueries = null; 479 | var testQuery = null; 480 | var testFolder = null; 481 | var myBugsQuery = null; 482 | var testProject = null; 483 | 484 | before( function( done ) { 485 | client.getProjects( function( err, projects ) { 486 | // console.log(err); 487 | // console.log(projects); 488 | testProject = _.find( projects, function( p ) { 489 | return p.name === testProjectName; 490 | } ); 491 | done(); 492 | } ); 493 | } ); 494 | 495 | it( 'returns a list of queries', function( done ) { 496 | should.exist( testProject ); 497 | client.getQueries( testProject.name, 2, function( err, queries ) { 498 | if ( err ) { 499 | console.log( err ); 500 | console.log( queries ); 501 | } 502 | should.not.exist( err ); 503 | should.exist( queries ); 504 | // console.log(queries); 505 | queries.length.should.be.above( 0 ); 506 | var folder = _.find( queries, function( q ) { 507 | return q.name === 'My Queries'; 508 | } ); 509 | should.exist( folder ); 510 | myQueries = folder; 511 | var sharedFolder = _.find( queries, function( q ) { 512 | return q.name === 'Shared Queries'; 513 | } ); 514 | should.exist( sharedFolder ); 515 | sharedFolder.children.should.be.instanceOf( Array ); 516 | sharedFolder.children.length.should.be.above( 0 ); 517 | // console.log(sharedFolder); 518 | var query = _.find( sharedFolder.children, function( q ) { 519 | return q.name === 'My Bugs'; 520 | } ); 521 | should.exist( query ); 522 | // console.log( query ); 523 | query.should.have.property( 'id' ); 524 | query.should.have.property( 'name' ); 525 | query.should.have.property( 'url' ); 526 | query.should.have.property( 'path' ); 527 | myBugsQuery = query; 528 | done(); 529 | } ); 530 | } ); 531 | 532 | it( 'returns a list of work items from saved query', function( done ) { 533 | should.exist( testProject ); 534 | should.exist( myBugsQuery ); 535 | 536 | client.getWorkItemIdsByQuery( myBugsQuery.id, testProject.name, function( err, ids ) { 537 | if ( err ) { 538 | console.log( err ); 539 | console.log( ids ); 540 | } 541 | ids.should.be.instanceOf( Array ); 542 | ids.length.should.be.above( 0 ); 543 | // console.log(ids); 544 | done(); 545 | } ); 546 | } ); 547 | 548 | it( 'returns a query by id', function( done ) { 549 | should.exist( testProject ); 550 | should.exist( myBugsQuery ); 551 | 552 | client.getQuery( testProject.name, myBugsQuery.id, function( err, query ) { 553 | if ( err ) { 554 | console.log( err ); 555 | console.log( query ); 556 | } 557 | should.not.exist( err ); 558 | should.exist( query ); 559 | //console.log(query); 560 | query.should.have.property( 'id' ); 561 | query.should.have.property( 'name' ); 562 | 563 | query.should.have.property( 'url' ); 564 | done(); 565 | } ); 566 | } ); 567 | 568 | it( 'creates a query folder', function( done ) { 569 | testFolder = null; 570 | should.exist( testProject ); 571 | should.exist( myQueries ); 572 | client.createFolder( testProject.name, 'testFolder1', myQueries.id, function( err, folder ) { 573 | should.not.exist( err ); 574 | should.exist( folder ); 575 | // console.log(folder); 576 | folder.should.have.property( 'id' ); 577 | folder.should.have.property( 'url' ); 578 | testFolder = folder; 579 | done(); 580 | } ); 581 | } ); 582 | 583 | it( 'creates a query', function( done ) { 584 | testQuery = null; 585 | should.exist( testProject ); 586 | should.exist( testFolder ); 587 | 588 | var wiql = 'Select [System.Id], [System.Title], [System.State] From WorkItems Where [System.WorkItemType] = \'Bug\' order by [Microsoft.VSTS.Common.Priority] asc, [System.CreatedDate] desc'; 589 | 590 | client.createQuery( testProject.name, 'testQuery1', testFolder.id, wiql, function( err, query ) { 591 | should.not.exist( err ); 592 | should.exist( query ); 593 | //console.log(query); 594 | query.should.have.property( 'id' ); 595 | query.should.have.property( 'url' ); 596 | testQuery = query; 597 | done(); 598 | } ); 599 | } ); 600 | 601 | it( 'updates a query by name', function( done ) { 602 | should.exist( testProject ); 603 | should.exist( testFolder ); 604 | should.exist( testQuery ); 605 | 606 | var wiql = 'Select [System.Id], [System.Title], [System.State] From WorkItems Where [System.WorkItemType] = \'Bug\' order by [Microsoft.VSTS.Common.Priority] asc, [System.CreatedDate] asc'; 607 | 608 | client.getQuery( testProject.name, testQuery.id, function( err, query ) { 609 | should.exist( query ); 610 | // console.log(testFolder); 611 | // console.log(query); 612 | client.updateQuery( testProject.name, query.name, 'testQuery1-updated', testFolder.path, wiql, function( err, query2 ) { 613 | should.not.exist( err ); 614 | should.exist( query2 ); 615 | // console.log(query); 616 | query2.should.have.property( 'id' ); 617 | query2.should.have.property( 'url' ); 618 | testQuery = query2; 619 | done(); 620 | } ); 621 | } ); 622 | } ); 623 | 624 | it( 'updates a query by id', function( done ) { 625 | should.exist( testProject ); 626 | should.exist( testFolder ); 627 | should.exist( testQuery ); 628 | 629 | var wiql = 'Select [System.Id], [System.Title], [System.State] From WorkItems Where [System.WorkItemType] = \'Bug\' order by [Microsoft.VSTS.Common.Priority] asc, [System.CreatedDate] asc'; 630 | 631 | client.getQuery( testProject.name, testQuery.id, function( err, query ) { 632 | should.exist( query ); 633 | // console.log(testFolder); 634 | // console.log(query); 635 | client.updateQuery( testProject.name, query.id, 'testQuery1-updated2', null, wiql, function( err, query2 ) { 636 | should.not.exist( err ); 637 | should.exist( query2 ); 638 | // console.log(query); 639 | query2.should.have.property( 'id' ); 640 | query2.should.have.property( 'url' ); 641 | testQuery = query2; 642 | done(); 643 | } ); 644 | } ); 645 | } ); 646 | 647 | it( 'deletes a query', function( done ) { 648 | should.exist( testProject ); 649 | should.exist( testQuery ); 650 | client.deleteQuery( testProject.name, testQuery.id, function( err, query ) { 651 | should.not.exist( err ); 652 | should.exist( query ); 653 | done(); 654 | } ); 655 | } ); 656 | 657 | it( 'deletes a query folder', function( done ) { 658 | should.exist( testProject ); 659 | should.exist( testFolder ); 660 | client.deleteFolder( testProject.name, testFolder.id, function( err, folder ) { 661 | should.not.exist( err ); 662 | should.exist( folder ); 663 | // console.log(folder); 664 | done(); 665 | } ); 666 | } ); 667 | 668 | } ); 669 | 670 | describe( 'work items', function() { 671 | var testItemIds = null; 672 | var testItemIdArray = null; 673 | var testItemId = null; 674 | 675 | // --------------------------------------- 676 | // Work Item Queries 677 | // --------------------------------------- 678 | 679 | it( 'returns a list of work items from wiql query', function( done ) { 680 | var wiql = 'Select [System.Id], [System.Title], [System.State] From WorkItems Where [System.WorkItemType] = \'Task\' order by [Microsoft.VSTS.Common.Priority] asc, [System.CreatedDate] desc'; 681 | 682 | client.getWorkItemIds( wiql, testProjectName, function( err, ids ) { 683 | if ( err ) { 684 | console.log( err ); 685 | console.log( ids ); 686 | } 687 | should.not.exist( err ); 688 | should.exist( ids ); 689 | //console.log(ids); 690 | ids.should.be.instanceOf( Array ); 691 | ids.length.should.be.above( 0 ); 692 | testItemIdArray = ids; 693 | testItemIds = ids.join( ',' ); 694 | testItemId = ids[ 0 ]; 695 | done(); 696 | } ); 697 | } ); 698 | 699 | it( 'returns a list of work items by comma-separated id list', function( done ) { 700 | client.getWorkItemsById( testItemIds, null, null, null, function( err, items ) { 701 | if ( err ) { 702 | console.log( err ); 703 | console.log( items ); 704 | } 705 | should.not.exist( err ); 706 | should.exist( items ); 707 | items.length.should.be.above( 0 ); 708 | var item = items[ 0 ]; 709 | item.should.have.property( 'id' ); 710 | item.should.have.property( 'rev' ); 711 | item.should.have.property( 'url' ); 712 | should.exist( item.fields ); 713 | // console.log( item.fields ); 714 | item.fields.should.have.property( 'System.Title' ); 715 | item.fields.should.have.property( 'System.State' ); 716 | done(); 717 | } ); 718 | } ); 719 | 720 | it( 'returns a list of work items by array of ids', function( done ) { 721 | client.getWorkItemsById( testItemIdArray, null, null, null, function( err, items ) { 722 | if ( err ) { 723 | console.log( err ); 724 | console.log( items ); 725 | } 726 | should.not.exist( err ); 727 | should.exist( items ); 728 | items.length.should.equal( testItemIdArray.length ); 729 | var item = items[ 0 ]; 730 | item.should.have.property( 'id' ); 731 | item.should.have.property( 'rev' ); 732 | item.should.have.property( 'url' ); 733 | should.exist( item.fields ); 734 | // console.log( item.fields ); 735 | item.fields.should.have.property( 'System.Title' ); 736 | item.fields.should.have.property( 'System.State' ); 737 | done(); 738 | } ); 739 | } ); 740 | 741 | it( 'returns a list of work items by ids with expanded links', function( done ) { 742 | client.getWorkItemsById( testItemIds, null, null, 'all', function( err, items ) { 743 | if ( err ) { 744 | console.log( err ); 745 | console.log( items ); 746 | } 747 | should.not.exist( err ); 748 | should.exist( items ); 749 | // console.log(items); 750 | items.length.should.be.above( 0 ); 751 | var item = items[ 0 ]; 752 | item.should.have.property( '_links' ); 753 | should.exist( item._links ); 754 | item._links.should.have.property( 'self' ); 755 | testItemId = item.id; 756 | done(); 757 | } ); 758 | } ); 759 | 760 | it( 'returns a work item by id', function( done ) { 761 | should.exist( testItemId ); 762 | client.getWorkItem( testItemId, 'all', function( err, item ) { 763 | //client.getWorkItem(7, 'all', function(err, item) { 764 | if ( err ) { 765 | console.log( err ); 766 | console.log( item ); 767 | } 768 | should.not.exist( err ); 769 | should.exist( item ); 770 | // console.log(item); 771 | item.should.have.property( 'id' ); 772 | item.should.have.property( 'rev' ); 773 | item.should.have.property( 'url' ); 774 | should.exist( item.fields ); 775 | // console.log( item.fields ); 776 | item.fields.should.have.property( 'System.Id' ); 777 | item.fields.should.have.property( 'System.Title' ); 778 | item.fields.should.have.property( 'System.State' ); 779 | done(); 780 | } ); 781 | } ); 782 | 783 | it( 'returns work item updates', function( done ) { 784 | client.getWorkItemUpdates( testItemId, function( err, updates ) { 785 | if ( err ) { 786 | console.log( err ); 787 | console.log( updates ); 788 | } 789 | should.not.exist( err ); 790 | should.exist( updates ); 791 | //console.log(updates); 792 | updates.length.should.be.above( 0 ); 793 | var update = updates[ updates.length - 1 ]; 794 | // console.log(update); 795 | update.should.have.property( 'id' ); 796 | update.should.have.property( 'rev' ); 797 | should.exist( update.fields ); 798 | done(); 799 | } ); 800 | } ); 801 | 802 | it( 'returns a page of work item updates', function( done ) { 803 | client.getWorkItemUpdates( testItemId, 2, 0, function( err, updates ) { 804 | if ( err ) { 805 | console.log( err ); 806 | console.log( updates ); 807 | } 808 | should.not.exist( err ); 809 | should.exist( updates ); 810 | //console.log(updates); 811 | updates.length.should.equal( 2 ); 812 | updates[ updates.length - 1 ].rev.should.be.above( 1 ); 813 | done(); 814 | } ); 815 | } ); 816 | 817 | it( 'returns a work item update by revision number', function( done ) { 818 | client.getWorkItemUpdate( testItemId, 1, function( err, update ) { 819 | if ( err ) { 820 | console.log( err ); 821 | console.log( update ); 822 | } 823 | should.not.exist( err ); 824 | should.exist( update ); 825 | //console.log(update); 826 | update.should.have.property( 'id' ); 827 | update.should.have.property( 'rev' ); 828 | update.should.have.property( 'url' ); 829 | should.exist( update.fields ); 830 | done(); 831 | } ); 832 | } ); 833 | 834 | it( 'returns a work item by revision number', function( done ) { 835 | client.getWorkItemRevision( testItemId, 1, function( err, item ) { 836 | if ( err ) { 837 | console.log( err ); 838 | console.log( item ); 839 | } 840 | should.not.exist( err ); 841 | should.exist( item ); 842 | //console.log(item); 843 | item.should.have.property( 'id' ); 844 | item.should.have.property( 'rev' ); 845 | item.should.have.property( 'url' ); 846 | should.exist( item.fields ); 847 | // console.log( item.fields ); 848 | item.fields.should.have.property( 'System.Title' ); 849 | item.fields.should.have.property( 'System.State' ); 850 | done(); 851 | } ); 852 | } ); 853 | 854 | } ); 855 | // Accounts and Profiles Tests are not testable since they required 856 | // OAuth, thus requiring human intervention to get a token with 857 | // an authorization 858 | 859 | describe.skip( 'Accounts', function() { 860 | it( 'should return a list of accounts', function( done ) { 861 | clientOAuth.getAccounts( memberId, function( err, accounts ) { 862 | // console.log(err); 863 | should.not.exist( err ); 864 | should.exist( accounts ); 865 | // console.log(accounts); 866 | accounts.should.be.instanceOf( Array ); 867 | accounts.length.should.be.above( 0 ); 868 | var account = accounts[ 0 ]; 869 | account.should.have.property( 'accountId' ); 870 | account.should.have.property( 'accountUri' ); 871 | account.should.have.property( 'accountName' ); 872 | account.should.have.property( 'organizationName' ); 873 | done(); 874 | } ); 875 | } ); 876 | } ); 877 | 878 | describe( 'team room tests', function() { 879 | var testTeamRoom = null; 880 | var testTeamRoomId = null; 881 | 882 | it( 'returns a list of team rooms', function( done ) { 883 | client.getRooms( function( err, rooms ) { 884 | should.not.exist( err ); 885 | should.exist( rooms ); 886 | // console.log( rooms ); 887 | rooms.length.should.be.above( 0 ); 888 | var room = _.find( rooms, function( t ) { 889 | return t.hasReadWritePermissions; 890 | } ); 891 | should.exist( room ); 892 | // console.log(testTeamRoom); 893 | room.should.have.property( 'id' ); 894 | room.should.have.property( 'name' ); 895 | room.should.have.property( 'description' ); 896 | room.should.have.property( 'lastActivity' ); 897 | room.should.have.property( 'createdBy' ); 898 | room.should.have.property( 'createdDate' ); 899 | room.should.have.property( 'hasAdminPermissions' ); 900 | room.should.have.property( 'hasReadWritePermissions' ); 901 | testTeamRoomId = room.id; 902 | done(); 903 | } ); 904 | } ); 905 | 906 | it( 'returns a room by id', function( done ) { 907 | if ( testTeamRoomId ) { 908 | client.getRoom( testTeamRoomId, function( err, room ) { 909 | should.not.exist( err ); 910 | should.exist( room ); 911 | // console.log( room ); 912 | room.should.have.property( 'id' ); 913 | room.should.have.property( 'name' ); 914 | room.should.have.property( 'description' ); 915 | room.should.have.property( 'lastActivity' ); 916 | room.should.have.property( 'createdBy' ); 917 | room.should.have.property( 'createdDate' ); 918 | room.should.have.property( 'hasAdminPermissions' ); 919 | room.should.have.property( 'hasReadWritePermissions' ); 920 | done(); 921 | } ); 922 | } else { 923 | console.log( 'Warning: no test team room' ); 924 | done(); 925 | } 926 | } ); 927 | 928 | it( 'creates a room', function( done ) { 929 | var randomName = 'test-room-' + ( Math.random() + 1 ).toString( 36 ).substring( 7 ); 930 | client.createRoom( randomName, 'a description', function( err, room ) { 931 | should.not.exist( err ); 932 | should.exist( room ); 933 | // console.log( room ); 934 | testTeamRoom = room; 935 | room.should.have.property( 'id' ); 936 | room.should.have.property( 'name' ); 937 | room.should.have.property( 'description' ); 938 | room.should.have.property( 'lastActivity' ); 939 | room.should.have.property( 'createdBy' ); 940 | room.should.have.property( 'createdDate' ); 941 | room.should.have.property( 'hasAdminPermissions' ); 942 | room.should.have.property( 'hasReadWritePermissions' ); 943 | done(); 944 | } ); 945 | } ); 946 | 947 | it ( 'updates a room', function( done ) { 948 | if ( testTeamRoom ) { 949 | var name = testTeamRoom.name + '-updated'; 950 | var description = 'updated description'; 951 | client.updateRoom( testTeamRoom.id, name, description, function( err, room ) { 952 | should.not.exist( err ); 953 | should.exist( room ); 954 | // console.log( room ); 955 | room.name.should.equal( name ); 956 | room.description.should.equal( description ); 957 | done(); 958 | } ); 959 | } else { 960 | console.log( 'Warning: no test team room' ); 961 | done(); 962 | } 963 | } ); 964 | 965 | it( 'deletes a room', function( done ) { 966 | if ( testTeamRoom ) { 967 | client.deleteRoom( testTeamRoom.id, function( err, res ) { 968 | should.not.exist( err ); 969 | // console.log( res ); 970 | done(); 971 | } ); 972 | } else { 973 | console.log( 'Warning: no test team room' ); 974 | done(); 975 | } 976 | } ); 977 | } ); 978 | 979 | describe( 'version control tests', function() { 980 | 981 | before( function() { 982 | client.setVersion( '1.0' ); 983 | } ); 984 | 985 | // --------------------------------------- 986 | // Version Control 987 | // --------------------------------------- 988 | 989 | it( 'returns root branches', function( done ) { 990 | client.getRootBranches( true, true, function( err, branches ) { 991 | if ( err ) { 992 | console.log( err, branches ); 993 | } 994 | should.not.exist( err ); 995 | should.exist( branches ); 996 | // console.log( branches ); 997 | done(); 998 | } ); 999 | } ); 1000 | 1001 | it.skip( 'returns a branch', function( done ) { 1002 | var path = '$/TestProject'; 1003 | client.getBranch( path, true, true, true, function( err, branch ) { 1004 | if ( err ) { 1005 | console.log( err, branch ); 1006 | } 1007 | should.not.exist( err ); 1008 | should.exist( branch ); 1009 | // console.log(branch); 1010 | done(); 1011 | } ); 1012 | } ); 1013 | 1014 | it( 'returns shelvesets', function( done ) { 1015 | client.getShelveSets( function( err, shelvesets ) { 1016 | if ( err ) { 1017 | console.log( err, shelvesets ); 1018 | } 1019 | should.not.exist( err ); 1020 | should.exist( shelvesets ); 1021 | // console.log(shelvesets); 1022 | done(); 1023 | } ); 1024 | } ); 1025 | 1026 | var testCommitId = null; 1027 | var testChangeSet = null; 1028 | 1029 | it( 'returns changesets', function( done ) { 1030 | client.getChangeSets( function( err, changesets ) { 1031 | if ( err ) { 1032 | console.log( err, changesets ); 1033 | } 1034 | should.not.exist( err ); 1035 | should.exist( changesets ); 1036 | // console.log(changesets); 1037 | changesets.should.be.instanceOf( Array ); 1038 | changesets.length.should.be.above( 0 ); 1039 | var changeset = changesets[ 0 ]; 1040 | changeset.should.have.property( 'changesetId' ); 1041 | changeset.should.have.property( 'url' ); 1042 | changeset.should.have.property( 'createdDate' ); 1043 | changeset.should.have.property( 'comment' ); 1044 | changeset.author.should.be.instanceOf( Object ); 1045 | changeset.checkedInBy.should.be.instanceOf( Object ); 1046 | testCommitId = changeset.changesetId; 1047 | done(); 1048 | } ); 1049 | } ); 1050 | 1051 | it( 'returns a changeset by id', function( done ) { 1052 | if ( testCommitId ) { 1053 | client.getChangeSet( testCommitId, function( err, changeset ) { 1054 | if ( err ) { 1055 | console.log( err, changeset ); 1056 | } 1057 | should.not.exist( err ); 1058 | should.exist( changeset ); 1059 | // console.log(changeset); 1060 | changeset.should.have.property( 'changesetId' ); 1061 | changeset.should.have.property( 'url' ); 1062 | changeset.should.have.property( 'createdDate' ); 1063 | changeset.should.have.property( 'comment' ); 1064 | changeset.author.should.be.instanceOf( Object ); 1065 | changeset.checkedInBy.should.be.instanceOf( Object ); 1066 | testChangeSet = changeset; 1067 | done(); 1068 | } ); 1069 | } else { 1070 | console.log( 'Warning: no test change set' ); 1071 | done(); 1072 | } 1073 | } ); 1074 | 1075 | it( 'returns changsets from range of IDs', function( done ) { 1076 | if ( testChangeSet ) { 1077 | var toId = testChangeSet.changesetId; 1078 | var fromId = toId - 2; 1079 | var expectedCount = 3; 1080 | if ( fromId < 1 ) { 1081 | fromId = 1; 1082 | expectedCount = toId - fromId + 1; 1083 | } 1084 | var queryOptions = { 1085 | fromId: fromId, 1086 | toId: testChangeSet.changesetId 1087 | }; 1088 | client.getChangeSets( queryOptions, function( err, changesets ) { 1089 | if ( err ) { 1090 | console.log( err, changesets ); 1091 | } 1092 | should.not.exist( err ); 1093 | should.exist( changesets ); 1094 | // console.log(changesets); 1095 | changesets.should.be.instanceOf( Array ); 1096 | changesets.length.should.equal( expectedCount ); 1097 | var changeset = changesets[ 0 ]; 1098 | changeset.should.have.property( 'changesetId' ); 1099 | changeset.should.have.property( 'url' ); 1100 | changeset.should.have.property( 'createdDate' ); 1101 | changeset.should.have.property( 'comment' ); 1102 | changeset.author.should.be.instanceOf( Object ); 1103 | changeset.checkedInBy.should.be.instanceOf( Object ); 1104 | done(); 1105 | } ); 1106 | } else { 1107 | console.log( 'Warning: no test change set' ); 1108 | done(); 1109 | } 1110 | } ); 1111 | 1112 | it( 'returns a changeset by id with details', function( done ) { 1113 | if ( testCommitId ) { 1114 | var queryOptions = { 1115 | includeDetails: true, 1116 | includeWorkItems: false, 1117 | maxChangeCount: 0, 1118 | maxCommentLength: 1000 1119 | }; 1120 | client.getChangeSet( testCommitId, queryOptions, function( err, changeset ) { 1121 | if ( err ) { 1122 | console.log( err, changeset ); 1123 | } 1124 | should.not.exist( err ); 1125 | should.exist( changeset ); 1126 | // console.log(changeset); 1127 | changeset.checkinNotes.should.be.instanceOf( Array ); 1128 | changeset.policyOverride.should.be.instanceOf( Object ); 1129 | done(); 1130 | } ); 1131 | } else { 1132 | console.log( 'Warning: no test change set' ); 1133 | done(); 1134 | } 1135 | } ); 1136 | 1137 | it( 'returns a changeset by id with work items', function( done ) { 1138 | if ( testCommitId ) { 1139 | var queryOptions = { 1140 | includeDetails: false, 1141 | includeWorkItems: true, 1142 | maxChangeCount: 0, 1143 | maxCommentLength: 1000 1144 | }; 1145 | client.getChangeSet( testCommitId, queryOptions, function( err, changeset ) { 1146 | if ( err ) { 1147 | console.log( err, changeset ); 1148 | } 1149 | should.not.exist( err ); 1150 | should.exist( changeset ); 1151 | // console.log(changeset); 1152 | changeset.workItems.should.be.instanceOf( Array ); 1153 | done(); 1154 | } ); 1155 | } else { 1156 | console.log( 'Warning: no test change set' ); 1157 | done(); 1158 | } 1159 | } ); 1160 | 1161 | it.skip( 'returns latest changeset changes', function( done ) { 1162 | client.getChangeSetChanges( function( err, changes ) { 1163 | if ( err ) { 1164 | console.log( err, changes ); 1165 | } 1166 | should.not.exist( err ); 1167 | should.exist( changes ); 1168 | // console.log(changes); 1169 | changes.should.be.instanceOf( Array ); 1170 | done(); 1171 | } ); 1172 | } ); 1173 | 1174 | it( 'returns changes for a changeset by id', function( done ) { 1175 | if ( testCommitId ) { 1176 | var queryOptions = { 1177 | id: testCommitId 1178 | }; 1179 | client.getChangeSetChanges( queryOptions, function( err, changes ) { 1180 | if ( err ) { 1181 | console.log( err, changes ); 1182 | } 1183 | should.not.exist( err ); 1184 | should.exist( changes ); 1185 | // console.log(changes); 1186 | changes.should.be.instanceOf( Array ); 1187 | done(); 1188 | } ); 1189 | } else { 1190 | console.log( 'Warning: no test change set' ); 1191 | done(); 1192 | } 1193 | } ); 1194 | 1195 | it.skip( 'returns latest changeset work items', function( done ) { 1196 | client.getChangeSetWorkItems( function( err, workitems ) { 1197 | if ( err ) { 1198 | console.log( err, workitems ); 1199 | } 1200 | should.not.exist( err ); 1201 | should.exist( workitems ); 1202 | // console.log(workitems); 1203 | workitems.should.be.instanceOf( Array ); 1204 | done(); 1205 | } ); 1206 | } ); 1207 | 1208 | it( 'returns work items for a changeset by id', function( done ) { 1209 | if ( testCommitId ) { 1210 | var queryOptions = { 1211 | id: testCommitId 1212 | }; 1213 | client.getChangeSetWorkItems( queryOptions, function( err, workitems ) { 1214 | if ( err ) { 1215 | console.log( err, workitems ); 1216 | } 1217 | should.not.exist( err ); 1218 | should.exist( workitems ); 1219 | // console.log(workitems); 1220 | workitems.should.be.instanceOf( Array ); 1221 | done(); 1222 | } ); 1223 | } else { 1224 | console.log( 'Warning: no test change set' ); 1225 | done(); 1226 | } 1227 | } ); 1228 | 1229 | it( 'gets a list of labels', function( done ) { 1230 | client.getLabels( function( err, labels ) { 1231 | if ( err ) { 1232 | console.log( err, labels ); 1233 | } 1234 | should.not.exist( err ); 1235 | should.exist( labels ); 1236 | // console.log(labels); 1237 | labels.should.be.instanceOf( Array ); 1238 | done(); 1239 | } ); 1240 | } ); 1241 | } ); 1242 | 1243 | describe.skip ( 'git repository tests', function() { 1244 | 1245 | // --------------------------------------- 1246 | // Git Repositories 1247 | // --------------------------------------- 1248 | 1249 | var testRepoName = null; 1250 | var testRepository = null; 1251 | var testGitProject = null; 1252 | var testCommit = null; 1253 | var testProject = null; 1254 | 1255 | before( function( done ) { 1256 | testRepoName = 'testRepo-' + ( Math.random() + 1 ).toString( 36 ).substring( 7 ); 1257 | client.getProjects( function( err, projects ) { 1258 | testProject = _.find( projects, function( p ) { 1259 | return p.name === testProjectName; 1260 | } ); 1261 | 1262 | // Find a project that uses git 1263 | if ( projects && projects.length > 0 ) { 1264 | async.each( projects, function( project, callback ) { 1265 | client.getProject( project.id, true, function( err, proj ) { 1266 | if ( proj.capabilities.versioncontrol.sourceControlType === 'Git' ) { 1267 | testGitProject = proj; 1268 | callback( 'Found' ); 1269 | } else { 1270 | callback(); 1271 | } 1272 | } ); 1273 | }, function( err ) { 1274 | if ( err !== 'Found' ) { 1275 | console.log( err ); 1276 | } 1277 | done(); 1278 | } ); 1279 | } 1280 | } ); 1281 | } ); 1282 | 1283 | it( 'creates a git repository', function( done ) { 1284 | if ( testGitProject ) { 1285 | client.createRepository( testGitProject.id, testRepoName, function( err, repository ) { 1286 | should.not.exist( err ); 1287 | should.exist( repository ); 1288 | testRepository = repository; 1289 | done(); 1290 | } ); 1291 | } else { 1292 | console.log( 'Warning: missing test project' ); 1293 | done(); 1294 | } 1295 | } ); 1296 | 1297 | it( 'returns a list of repositories', function( done ) { 1298 | client.getRepositories( function( err, repositories ) { 1299 | should.not.exist( err ); 1300 | should.exist( repositories ); 1301 | // console.log(repositories); 1302 | if ( repositories.length > 0 ) { 1303 | var repository = repositories[ 0 ]; 1304 | repository.should.have.property( 'id' ); 1305 | repository.should.have.property( 'name' ); 1306 | repository.should.have.property( 'remoteUrl' ); 1307 | repository.project.should.be.instanceOf( Object ); 1308 | } 1309 | done(); 1310 | } ); 1311 | } ); 1312 | 1313 | it( 'returns a list of repositories by project', function( done ) { 1314 | if ( testGitProject ) { 1315 | client.getRepositories( testGitProject.id, function( err, repositories ) { 1316 | should.not.exist( err ); 1317 | should.exist( repositories ); 1318 | repositories.length.should.be.above( 0 ); 1319 | done(); 1320 | } ); 1321 | } else { 1322 | console.log( 'Warning: missing test project' ); 1323 | done(); 1324 | } 1325 | } ); 1326 | 1327 | it( 'returns a repository by id', function( done ) { 1328 | if ( testRepository ) { 1329 | client.getRepository( testRepository.id, function( err, repository ) { 1330 | should.not.exist( err ); 1331 | should.exist( repository ); 1332 | repository.should.have.property( 'id' ); 1333 | repository.should.have.property( 'name' ); 1334 | repository.should.have.property( 'remoteUrl' ); 1335 | repository.project.should.be.instanceOf( Object ); 1336 | done(); 1337 | } ); 1338 | } else { 1339 | console.log( 'Warning: missing test repository' ); 1340 | done(); 1341 | } 1342 | } ); 1343 | 1344 | it( 'returns a repository by name', function( done ) { 1345 | if ( testRepository ) { 1346 | client.getRepository( testRepository.name, testProject.id, function( err, repository ) { 1347 | should.not.exist( err ); 1348 | should.exist( repository ); 1349 | repository.should.have.property( 'id' ); 1350 | repository.should.have.property( 'name' ); 1351 | repository.should.have.property( 'remoteUrl' ); 1352 | repository.project.should.be.instanceOf( Object ); 1353 | done(); 1354 | } ); 1355 | } else { 1356 | console.log( 'Warning: missing test repository' ); 1357 | done(); 1358 | } 1359 | } ); 1360 | 1361 | it( 'renames a repository', function( done ) { 1362 | if ( testRepository ) { 1363 | client.renameRepository( testRepository.id, testRepository.name + '-update', function( err, repository ) { 1364 | should.not.exist( err ); 1365 | should.exist( repository ); 1366 | repository.should.have.property( 'id' ); 1367 | repository.should.have.property( 'name' ); 1368 | repository.should.have.property( 'remoteUrl' ); 1369 | repository.project.should.be.instanceOf( Object ); 1370 | repository.name.should.equal( testRepository.name + '-update' ); 1371 | done(); 1372 | } ); 1373 | } else { 1374 | console.log( 'Warning: missing test repository' ); 1375 | done(); 1376 | } 1377 | } ); 1378 | 1379 | it( 'deletes a repository', function( done ) { 1380 | if ( testRepository ) { 1381 | client.deleteRepository( testRepository.id, function( err, repository ) { 1382 | should.not.exist( err ); 1383 | should.exist( repository ); 1384 | done(); 1385 | } ); 1386 | } else { 1387 | console.log( 'Warning: missing test repository' ); 1388 | done(); 1389 | } 1390 | } ); 1391 | 1392 | it( 'gets a list of commits', function( done ) { 1393 | if ( testGitProject ) { 1394 | client.getRepositories( testGitProject.id, function( err, repositories ) { 1395 | should.not.exist( err ); 1396 | should.exist( repositories ); 1397 | if ( repositories.length > 0 ) { 1398 | // console.log( repositories ); 1399 | var repository = repositories[ 0 ]; 1400 | testRepository = repository; 1401 | console.log( repository ); 1402 | client.getCommits( repository.id, function( err, commits ) { 1403 | should.not.exist( err ); 1404 | should.exist( commits ); 1405 | commits.should.be.instanceOf( Array ); 1406 | console.log( commits ); 1407 | if ( commits.length > 0 ) { 1408 | var commit = commits[ 0 ]; 1409 | commit.should.have.property( 'commitId' ); 1410 | commit.should.have.property( 'comment' ); 1411 | commit.should.have.property( 'url' ); 1412 | commit.author.should.be.instanceOf( Object ); 1413 | commit.committer.should.be.instanceOf( Object ); 1414 | commit.changeCounts.should.be.instanceOf( Object ); 1415 | testCommit = commit; 1416 | } 1417 | // console.log(commits); 1418 | done(); 1419 | } ); 1420 | } else { 1421 | conosole.log( 'Warning: no repositories in project', testProject ); 1422 | done(); 1423 | } 1424 | } ); 1425 | } else { 1426 | console.log( 'Warning: missing test project' ); 1427 | done(); 1428 | } 1429 | } ); 1430 | 1431 | it( 'gets a list of commits by author', function( done ) { 1432 | if ( testRepository && testCommit ) { 1433 | client.getCommits( testRepository.id, null, null, testCommit.author.name, function( err, commits ) { 1434 | should.not.exist( err ); 1435 | should.exist( commits ); 1436 | commits.should.be.instanceOf( Array ); 1437 | commits.length.should.be.above( 0 ); 1438 | done(); 1439 | } ); 1440 | } else { 1441 | console.log( 'Warning: missing test repository and commit' ); 1442 | done(); 1443 | } 1444 | } ); 1445 | 1446 | it( 'gets a commit by id', function( done ) { 1447 | if ( testRepository && testCommit ) { 1448 | client.getCommit( testRepository.id, testCommit.commitId, function( err, commit ) { 1449 | should.not.exist( err ); 1450 | should.exist( commit ); 1451 | commit.parents.should.be.instanceOf( Array ); 1452 | commit.should.have.property( 'treeId' ); 1453 | commit.push.should.be.instanceOf( Object ); 1454 | commit.should.have.property( 'commitId' ); 1455 | commit.should.have.property( 'comment' ); 1456 | commit.should.have.property( 'url' ); 1457 | commit.author.should.be.instanceOf( Object ); 1458 | commit.committer.should.be.instanceOf( Object ); 1459 | should.not.exist( commit.changes ); 1460 | // console.log(commit); 1461 | done(); 1462 | } ); 1463 | } else { 1464 | console.log( 'Warning: missing test repository and commit' ); 1465 | done(); 1466 | } 1467 | } ); 1468 | 1469 | it( 'gets a commit by id with changed items', function( done ) { 1470 | if ( testRepository && testCommit ) { 1471 | client.getCommit( testRepository.id, testCommit.commitId, 10, function( err, commit ) { 1472 | should.not.exist( err ); 1473 | should.exist( commit ); 1474 | // console.log(commit); 1475 | commit.changes.should.be.instanceOf( Array ); 1476 | commit.changeCounts.should.be.instanceOf( Object ); 1477 | done(); 1478 | } ); 1479 | } else { 1480 | console.log( 'Warning: missing test repository and commit' ); 1481 | done(); 1482 | } 1483 | } ); 1484 | 1485 | it( 'gets a list of commit diffs', function( done ) { 1486 | if ( testRepository ) { 1487 | client.getDiffs( testRepository.id, null, 'master', null, 'develop', function( err, diffs ) { 1488 | should.not.exist( err ); 1489 | should.exist( diffs ); 1490 | diffs.should.have.property( 'allChangesIncluded' ); 1491 | diffs.changes.should.be.instanceOf( Array ); 1492 | diffs.should.have.property( 'commonCommit' ); 1493 | diffs.should.have.property( 'aheadCount' ); 1494 | diffs.should.have.property( 'behindCount' ); 1495 | // console.log(diffs); 1496 | done(); 1497 | } ); 1498 | } else { 1499 | console.log( 'Warning: missing test repository' ); 1500 | done(); 1501 | } 1502 | } ); 1503 | 1504 | it( 'gets a list of pushes', function( done ) { 1505 | if ( testRepository ) { 1506 | client.getPushes( testRepository.id, function( err, pushes ) { 1507 | should.not.exist( err ); 1508 | should.exist( pushes ); 1509 | pushes.should.be.instanceOf( Array ); 1510 | pushes.length.should.be.above( 0 ); 1511 | // console.log(pushes); 1512 | done(); 1513 | } ); 1514 | } else { 1515 | console.log( 'Warning: missing test repository' ); 1516 | done(); 1517 | } 1518 | } ); 1519 | 1520 | it( 'gets stats for repository', function( done ) { 1521 | if ( testRepository ) { 1522 | client.getStats( testRepository.id, function( err, stats ) { 1523 | should.not.exist( err ); 1524 | should.exist( stats ); 1525 | stats.should.be.instanceOf( Array ); 1526 | stats.length.should.be.above( 0 ); 1527 | // console.log(stats); 1528 | done(); 1529 | } ); 1530 | } else { 1531 | console.log( 'Warning: missing test repository' ); 1532 | done(); 1533 | } 1534 | } ); 1535 | 1536 | it( 'gets refs for repository', function( done ) { 1537 | if ( testRepository ) { 1538 | client.getRefs( testRepository.id, function( err, refs ) { 1539 | should.not.exist( err ); 1540 | should.exist( refs ); 1541 | refs.should.be.instanceOf( Array ); 1542 | refs.length.should.be.above( 0 ); 1543 | // console.log(refs); 1544 | var ref = refs[ 0 ]; 1545 | ref.should.have.property( 'name' ); 1546 | ref.should.have.property( 'objectId' ); 1547 | ref.should.have.property( 'url' ); 1548 | done(); 1549 | } ); 1550 | } else { 1551 | console.log( 'Warning: missing test repository' ); 1552 | done(); 1553 | } 1554 | } ); 1555 | 1556 | } ); 1557 | 1558 | describe( 'Service Hook tests', function() { 1559 | 1560 | // --------------------------------------- 1561 | // Service Hooks 1562 | // --------------------------------------- 1563 | 1564 | var testProject = null; 1565 | var testSubscription = null; 1566 | 1567 | before( function( done ) { 1568 | client.getProjects( function( err, projects ) { 1569 | // console.log(err); 1570 | // console.log(projects); 1571 | testProject = _.find( projects, function( p ) { 1572 | return p.name === testProjectName; 1573 | } ); 1574 | done(); 1575 | } ); 1576 | } ); 1577 | 1578 | it( 'should get a list of publishers', function( done ) { 1579 | client.getConsumers( function( err, publishers ) { 1580 | if ( err ) console.log( err, publishers ); 1581 | should.not.exist( err ); 1582 | should.exist( publishers ); 1583 | // console.log(publishers); 1584 | publishers.should.be.instanceOf( Array ); 1585 | var publisher = _.find( publishers, function( c ) { 1586 | return c.id === 'webHooks'; 1587 | } ); 1588 | should.exist( publisher ); 1589 | // console.log(publisher); 1590 | // console.log(publisher.actions[0].inputDescriptors); 1591 | publisher.should.have.property( 'id' ); 1592 | publisher.should.have.property( 'url' ); 1593 | publisher.should.have.property( 'name' ); 1594 | publisher.should.have.property( 'description' ); 1595 | publisher.should.have.property( 'informationUrl' ); 1596 | publisher.should.have.property( 'authenticationType' ); 1597 | publisher.inputDescriptors.should.be.instanceOf( Array ); 1598 | publisher.actions.should.be.instanceOf( Array ); 1599 | done(); 1600 | } ); 1601 | } ); 1602 | 1603 | it( 'should get a list of consumers', function( done ) { 1604 | client.getConsumers( function( err, consumers ) { 1605 | if ( err ) console.log( err, consumers ); 1606 | should.not.exist( err ); 1607 | should.exist( consumers ); 1608 | // console.log(consumers); 1609 | consumers.should.be.instanceOf( Array ); 1610 | var zendesk = _.find( consumers, function( c ) { 1611 | return c.id === 'zendesk'; 1612 | } ); 1613 | should.exist( zendesk ); 1614 | // console.log(zendesk); 1615 | zendesk.should.have.property( 'id' ); 1616 | zendesk.should.have.property( 'url' ); 1617 | zendesk.should.have.property( 'name' ); 1618 | zendesk.should.have.property( 'description' ); 1619 | zendesk.should.have.property( 'informationUrl' ); 1620 | zendesk.should.have.property( 'authenticationType' ); 1621 | zendesk.inputDescriptors.should.be.instanceOf( Array ); 1622 | zendesk.actions.should.be.instanceOf( Array ); 1623 | done(); 1624 | } ); 1625 | } ); 1626 | 1627 | it( 'should get a consumer by id', function( done ) { 1628 | client.getConsumer( 'zapier', function( err, consumer ) { 1629 | if ( err ) console.log( err, consumer ); 1630 | should.not.exist( err ); 1631 | should.exist( consumer ); 1632 | // console.log(consumer); 1633 | consumer.should.have.property( 'id' ); 1634 | consumer.should.have.property( 'url' ); 1635 | consumer.should.have.property( 'name' ); 1636 | consumer.should.have.property( 'description' ); 1637 | consumer.should.have.property( 'informationUrl' ); 1638 | consumer.should.have.property( 'authenticationType' ); 1639 | consumer.inputDescriptors.should.be.instanceOf( Array ); 1640 | consumer.actions.should.be.instanceOf( Array ); 1641 | done(); 1642 | } ); 1643 | } ); 1644 | 1645 | it( 'should get a list of consumer actions by id', function( done ) { 1646 | client.getConsumerActions( 'zapier', function( err, actions ) { 1647 | if ( err ) console.log( err, actions ); 1648 | should.not.exist( err ); 1649 | should.exist( actions ); 1650 | // console.log(actions); 1651 | actions.should.be.instanceOf( Array ); 1652 | actions.length.should.be.above( 0 ); 1653 | var action = actions[ 0 ]; 1654 | // console.log(action); 1655 | action.should.have.property( 'id' ); 1656 | action.should.have.property( 'consumerId' ); 1657 | action.should.have.property( 'url' ); 1658 | action.should.have.property( 'name' ); 1659 | action.should.have.property( 'description' ); 1660 | action.inputDescriptors.should.be.instanceOf( Array ); 1661 | action.supportedEventTypes.should.be.instanceOf( Array ); 1662 | done(); 1663 | } ); 1664 | } ); 1665 | 1666 | it( 'should get a list of consumer action by id', function( done ) { 1667 | client.getConsumerAction( 'zapier', 'sendNotification', function( err, action ) { 1668 | if ( err ) console.log( err, action ); 1669 | should.not.exist( err ); 1670 | should.exist( action ); 1671 | // console.log(action); 1672 | action.should.have.property( 'id' ); 1673 | action.should.have.property( 'consumerId' ); 1674 | action.should.have.property( 'url' ); 1675 | action.should.have.property( 'name' ); 1676 | action.should.have.property( 'description' ); 1677 | action.inputDescriptors.should.be.instanceOf( Array ); 1678 | action.supportedEventTypes.should.be.instanceOf( Array ); 1679 | done(); 1680 | } ); 1681 | } ); 1682 | 1683 | it( 'should create a subscription', function( done ) { 1684 | should.exist( testProject ); 1685 | var subscription = { 1686 | eventType: 'workitem.created', 1687 | publisherId: 'tfs', 1688 | resourceVersion: '1.0', 1689 | eventDescription: 'Any work item', 1690 | consumerId: 'webHooks', 1691 | consumerActionId: 'httpRequest', 1692 | actionDescription: '...', 1693 | consumerInputs: { 1694 | url: 'https://localhost:1234/test/test-consumer/' + testProject.id 1695 | }, 1696 | publisherInputs: { 1697 | projectId: testProject.id 1698 | } 1699 | }; 1700 | 1701 | client.createSubscription( subscription, function( err, sub ) { 1702 | if ( err ) console.log( err, sub ); 1703 | should.not.exist( err ); 1704 | should.exist( sub ); 1705 | // console.log( sub ); 1706 | sub.should.have.property( 'id' ); 1707 | sub.should.have.property( 'url' ); 1708 | sub.should.have.property( 'eventType' ); 1709 | testSubscription = sub; 1710 | done(); 1711 | } ); 1712 | } ); 1713 | 1714 | it( 'should get a list of subscriptions', function( done ) { 1715 | client.getSubscriptions( function( err, subscriptions ) { 1716 | if ( err ) console.log( err, subscriptions ); 1717 | should.not.exist( err ); 1718 | should.exist( subscriptions ); 1719 | // console.log( subscriptions ); 1720 | subscriptions.should.be.instanceOf( Array ); 1721 | done(); 1722 | } ); 1723 | } ); 1724 | 1725 | it( 'deletes a subscription', function( done ) { 1726 | should.exist( testSubscription ); 1727 | client.deleteSubscription( testSubscription.id, function( err, res ) { 1728 | if ( err ) console.log( err, res ); 1729 | should.not.exist( err ); 1730 | should.exist( res ); 1731 | done(); 1732 | } ); 1733 | } ); 1734 | 1735 | it.skip( 'should get a list of subscriptions by query', function( done ) { 1736 | // Documentation incomplete 1737 | var queryOptions = { 1738 | publisherId: '', 1739 | eventType: '', 1740 | consumerId: '', 1741 | consumerActionId: '', 1742 | publisherInputFilters: [ { 1743 | conditions: [ { 1744 | inputId: '', 1745 | operator: 'equals', 1746 | inputValue: '' 1747 | } ] 1748 | } ] 1749 | }; 1750 | client.querySubscriptions( queryOptions, function( err, subscriptions ) { 1751 | if ( err ) console.log( err, subscriptions ); 1752 | should.not.exist( err ); 1753 | should.exist( subscriptions ); 1754 | // console.log(subscriptions); 1755 | subscriptions.should.be.instanceOf( Array ); 1756 | done(); 1757 | } ); 1758 | } ); 1759 | 1760 | 1761 | } ); 1762 | 1763 | describe.skip( 'Build tests', function() { 1764 | 1765 | it( 'should return a list of build definitions', function( done ) { 1766 | client.getBuildDefinitions( function( err, builds ) { 1767 | should.not.exist( err ); 1768 | should.exist( builds ); 1769 | builds.length.should.be.above( 0 ); 1770 | var build = builds[ 0 ]; 1771 | build.should.have.property( 'id' ); 1772 | build.should.have.property( 'name' ); 1773 | build.should.have.property( 'url' ); 1774 | done(); 1775 | } ); 1776 | } ); 1777 | 1778 | it( 'should queue a build', function( done ) { 1779 | var buildRequest = { definition: { id: 1 }, reason: 'Manual', priority: 'Normal' }; 1780 | // console.log(buildRequest); 1781 | client.queueBuild( buildRequest, function( err, buildResponse ) { 1782 | should.not.exist( err ); 1783 | should.exist( buildResponse ); 1784 | buildResponse.length.should.be.above( 0 ); 1785 | buildResponse.should.have.property( 'status' ); 1786 | done(); 1787 | } ); 1788 | } ); 1789 | } ); 1790 | 1791 | } ); 1792 | 1793 | describe( 'VSO Service Account Tests', function() { 1794 | this.timeout( 20000 ); 1795 | 1796 | it( 'has a WRAP valid client', function() { 1797 | var client = Client.createWrapClient( url, collection, "dummy wrap token", getOptions() ); 1798 | should.exist( client ); 1799 | client.should.have.property( 'url' ); 1800 | client.should.have.property( '_authType' ); 1801 | client._authType.should.equal( 'Wrap' ); 1802 | should.exist( client.client ); 1803 | } ); 1804 | 1805 | describe( 'Request WRAP Token Tests', function() { 1806 | 1807 | it( 'should return a WRAP valid token', function( done ) { 1808 | Client.getWrapToken( url, serviceAccountUser, serviceAccountPassword, function( err, data, resp ) { 1809 | should.not.exist( err ); 1810 | data.should.have.property( 'wrap_access_token' ); 1811 | data.should.have.property( 'wrap_access_token_expires_in' ); 1812 | done(); 1813 | } ); 1814 | } ); 1815 | 1816 | it( 'should return an error with 401', function( done ) { 1817 | Client.getWrapToken( url, "dummy user", "dummy password", function( err, data, resp ) { 1818 | should.exist( err ); 1819 | err.statusCode.should.equal( 401 ); 1820 | done(); 1821 | } ); 1822 | } ); 1823 | } ); 1824 | 1825 | describe( 'API calls with WRAP Token Tests', function() { 1826 | var client; 1827 | 1828 | before( function( done ) { 1829 | Client.getWrapToken( url, serviceAccountUser, serviceAccountPassword, function( err, data, resp ) { 1830 | if ( err ) { 1831 | console.log( "Can't get WRAP token" ); 1832 | throw err; 1833 | } 1834 | client = Client.createWrapClient( url, collection, data.wrap_access_token, getOptions() ); 1835 | done(); 1836 | } ); 1837 | } ); 1838 | 1839 | 1840 | it( 'should return a list of projects', function( done ) { 1841 | client.getProjects( function( err, projects ) { 1842 | should.not.exist( err ); 1843 | should.exist( projects ); 1844 | // console.log(projects); 1845 | projects.length.should.be.above( 0 ); 1846 | var project = _.find( projects, function( p ) { 1847 | return p.name === testProjectName; 1848 | } ); 1849 | project.should.have.property( 'id' ); 1850 | project.should.have.property( 'name' ); 1851 | project.should.have.property( 'url' ); 1852 | should.exist( project.collection ); 1853 | should.exist( project.defaultTeam ); 1854 | done(); 1855 | } ); 1856 | } ); 1857 | 1858 | } ); 1859 | } ); 1860 | -------------------------------------------------------------------------------- /src/vso-client.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'lodash' 2 | requestJson = require 'request-json' 3 | request = require "request" 4 | azure = require 'azure' 5 | 6 | apiVersion = '1.0' 7 | 8 | spsUri = 'https://app.vssps.visualstudio.com' 9 | vsoTokenUri = spsUri + '/oauth2/token' 10 | 11 | requestToken = (clientAssertion, assertion, grantType, redirectUri, callback, tokenUri) -> 12 | 13 | request.post tokenUri, { 14 | # proxy: "http://127.0.0.1:8888" , 15 | form : { 16 | "client_assertion_type" : "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" 17 | "client_assertion" : clientAssertion, 18 | "response_type" : "Assertion", 19 | "grant_type" : grantType, 20 | "assertion" : assertion, 21 | "redirect_uri" : redirectUri 22 | } 23 | } , (err, res, body) -> 24 | if (err) 25 | callback err, body 26 | else if (res.statusCode != 200 and res.statusCode != 400 and res.statusCode != 401) 27 | callback "Error Code " + res.statusCode, body 28 | else 29 | callback err, JSON.parse body 30 | 31 | 32 | requestWrapToken = (accountUrl, username, password, callback) -> 33 | request 34 | url: accountUrl, 35 | followRedirect: false 36 | (err, res, body) -> 37 | return callback err, body, res if err 38 | realm = res.headers['x-tfs-fedauthrealm'] 39 | issuer = res.headers['x-tfs-fedauthissuer'] 40 | 41 | return callback "Can't determine Federation data on headers", body, res unless realm and issuer 42 | 43 | wrapService = azure.createWrapService issuer, username, password 44 | wrapService.wrapAccessToken realm, (err, token, res) -> 45 | callback err, (if err then res.body else token), res 46 | 47 | 48 | exports.createClient = (url, collection, username, password, options) -> new exports.Client url, collection, (new AuthenticationCredential username, password), options 49 | exports.createOAuthClient = (url, collection, accessToken, options) -> new exports.Client url, collection, (new AuthenticationOAuth accessToken), options 50 | exports.createWrapClient = (url, collection, accessToken, options) -> new exports.Client url, collection, (new AuthenticationWrap accessToken), options 51 | 52 | exports.getToken = (clientAssertion, assertion, redirectUri, callback, tokenUri = vsoTokenUri) -> requestToken(clientAssertion, assertion, "urn:ietf:params:oauth:grant-type:jwt-bearer", redirectUri, callback, tokenUri) 53 | exports.refreshToken = (clientAssertion, assertion, redirectUri, callback, tokenUri = vsoTokenUri) -> requestToken(clientAssertion, assertion, "refresh_token", redirectUri, callback, tokenUri) 54 | exports.getWrapToken = (accountUrl, username, password, callback) -> requestWrapToken accountUrl, username, password, callback 55 | 56 | class AuthenticationCredential 57 | constructor: (@username, @password) -> 58 | @type = "Credential" 59 | 60 | class AuthenticationOAuth 61 | constructor: (@accessToken) -> 62 | @type = "OAuth" 63 | 64 | class AuthenticationWrap 65 | constructor: (@accessToken) -> 66 | @type = "Wrap" 67 | 68 | 69 | class exports.Client 70 | constructor: (@url, @collection, authentication, options) -> 71 | apiUrl = url 72 | @client = requestJson.newClient(apiUrl, options?.clientOptions) 73 | if (authentication is AuthenticationCredential || authentication.type == "Credential") 74 | @client.setBasicAuth authentication.username, authentication.password 75 | else if (authentication is AuthenticationOAuth || authentication.type == "OAuth") 76 | spsUrl = (options?.spsUri || spsUri) 77 | @clientSPS = requestJson.newClient(spsUrl, options?.clientOptions) 78 | @client.headers.Authorization = "bearer " + authentication.accessToken 79 | @clientSPS.headers.Authorization = "bearer " + authentication.accessToken 80 | else if (authentication is AuthenticationWrap || authentication.type == "Wrap") 81 | @client.headers.Authorization = "WRAP access_token=\"#{authentication.accessToken}\"" 82 | else 83 | throw new Error "unknown authentication type" 84 | @_authType = authentication.type 85 | @apiVersion = options?.apiVersion || apiVersion 86 | userAgent = 'vso-client/1.0 node/' + process.versions.node + ' ' + process.platform 87 | if (options?.userAgent?) 88 | @userAgent = options.userAgent + '; ' + userAgent 89 | else 90 | @userAgent = userAgent; 91 | 92 | parseReplyData: (error, res, body, callback) -> 93 | if error 94 | callback error, body 95 | else if @_authType == "OAuth" and res?.statusCode == 203 96 | callback "Error unauthorized. Check OAUth token", body 97 | else if res?.statusCode == 401 or (@_authType != "OAuth" and res?.statusCode == 203) 98 | callback "Error unauthorized", body 99 | else if res?.statusCode >= 500 && res?.statusCode < 600 100 | callback "Error call failed with HTTP Code " + res.statusCode, body 101 | else if (body.errorCode or body.errorCode is 0) and (body.message or body.typeKey) 102 | #console.log error, body 103 | err = 'Error ' + body.errorCode + ': ' 104 | if body.message and body.message.length > 0 105 | err += body.message 106 | else 107 | err += body.typeKey 108 | #console.log err, body 109 | callback err, body 110 | else if body and body.value 111 | #console.log err, body 112 | callback error, body.value 113 | else if body and body.id 114 | callback error, body 115 | else if body and body.length > 0 116 | #console.log body 117 | callback 'Unknown Error', body 118 | else 119 | callback error, body 120 | 121 | 122 | findItemField: (fields, fieldName) -> 123 | field = _.find fields, (f) -> 124 | f.field.refName is fieldName 125 | field 126 | 127 | findFirstItemField: (fields, fieldNames) -> 128 | field = null 129 | for fieldName in fieldNames 130 | field = @findItemField fields, fieldName 131 | if field 132 | break 133 | field 134 | 135 | setAccessToken : (acessToken) -> 136 | if (@_authType != "OAuth" and @_authType != "Wrap") 137 | throw new Error "can only set access token for OAuth or Wrap client" 138 | 139 | if @_authType is "OAuth" 140 | @client.headers.Authorization = "bearer #{acessToken}" 141 | else 142 | @client.headers.Authorization = "WRAP access_token=\"#{acessToken}\"" 143 | 144 | setVersion : (version) -> 145 | @apiVersion = version 146 | 147 | checkAndRequireOAuth : (methodName) -> 148 | if (@_authType != "OAuth") 149 | throw new Error methodName + " can only be invoked with OAuth" 150 | 151 | buildApiPath : (path, params, options) -> 152 | basePath = "" 153 | unless options?.excludeCollection 154 | if options?.projectName 155 | basePath = '/'+ @collection + '/' + (encodeURI options.projectName) 156 | else 157 | basePath = '/' + @collection 158 | 159 | returnPath = basePath + '/_apis/' + path # + '?api-version=' + @apiVersion 160 | 161 | if params and params.length > 0 162 | if (params[0] != '?') 163 | params = '?' + params 164 | returnPath += params 165 | returnPath 166 | 167 | getOptions : (patch) -> 168 | options = { headers: {} } 169 | contentType = if patch and @apiVersion isnt '1.0-preview.1' then 'application/json-patch+json' else 'application/json' 170 | 171 | options.headers['accept'] = 'application/json; api-version=' + @apiVersion 172 | options.headers['content-type'] = contentType 173 | options.headers['user-agent'] = @userAgent 174 | options 175 | 176 | getPatchContentType : -> 177 | return 'application/json' if @apiVersion == "1.0-preview.1" 178 | 179 | return 'application/json-patch+json' 180 | 181 | encodeFolderPath : (folderParam) -> 182 | return "" unless folderParam 183 | 184 | "/" + (folderParam.split("/").map (e) -> encodeURI e).join("/") 185 | 186 | isGuid : (id) -> 187 | guidPattern = /// ^ 188 | [0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12} 189 | $ ///i 190 | 191 | id.match guidPattern 192 | 193 | ######################################### 194 | # Version validation 195 | ######################################### 196 | 197 | getVersion = (version) -> 198 | dashPosition = version.indexOf("-") 199 | 200 | if(dashPosition != -1) 201 | return version.substring(0, dashPosition) 202 | 203 | return version 204 | 205 | getMinorVersion = (previewVersion) -> 206 | parts = previewVersion.split "." 207 | return parts[1] if parts.length > 1 208 | 209 | return "0" 210 | 211 | getVersionStage = (version) -> 212 | dashPosition = version.indexOf("-") 213 | 214 | if(dashPosition == - 1) 215 | return "" 216 | 217 | return version.substring dashPosition 218 | 219 | requireMinimumVersion : (version, requiredMinimumVersion) -> 220 | majorVersion = getVersion version 221 | majorRequiredVersion = getVersion requiredMinimumVersion 222 | 223 | # major defines everything. No preview parts 224 | return false if majorVersion < majorRequiredVersion 225 | return true if majorVersion > majorRequiredVersion 226 | 227 | previewVersion = getVersionStage version 228 | previewRequiredMinimumVersion = getVersionStage requiredMinimumVersion 229 | 230 | return majorVersion >= majorRequiredVersion if previewVersion == previewRequiredMinimumVersion == "" 231 | 232 | if previewVersion != "" and previewRequiredMinimumVersion != "" 233 | # major is equal. Just need to check preview minor 234 | return (getMinorVersion previewVersion) >= (getMinorVersion previewRequiredMinimumVersion) 235 | 236 | # If we reach here we know majors are the same. 237 | return true if previewVersion == "" and previewRequiredMinimumVersion != "" 238 | 239 | return false 240 | 241 | checkAndRequireMinimumVersion: (minimumVersion) -> 242 | unless @requireMinimumVersion @apiVersion , minimumVersion 243 | throw new Error "this method requires at least @{minimumVersion)" 244 | 245 | ######################################### 246 | # Projects and Teams 247 | ######################################### 248 | 249 | getProjects: (stateFilter, pageSize, skip, callback) -> 250 | # valid stateFilter values: WellFormed, CreatePending, Deleting, New, All 251 | 252 | if typeof stateFilter is 'function' 253 | callback = stateFilter 254 | stateFilter = pageSize = skip = null 255 | else if typeof pageSize is 'function' 256 | callback = pageSize 257 | pageSize = skip = null 258 | else if typeof skip is 'function' 259 | callback = skip 260 | skip = null 261 | 262 | pageSize = pageSize ? 100 263 | skip = skip ? 0 264 | stateFilter = stateFilter ? 'WellFormed' 265 | 266 | path = @buildApiPath 'projects', 'stateFilter=' + stateFilter + '&$top=' + pageSize + "&$skip=" + skip 267 | @client.get path, @getOptions(), (err, res, body) => 268 | @parseReplyData err, res, body, callback 269 | 270 | getProject: (projectId, includeCapabilities, callback) -> 271 | 272 | if typeof includeCapabilities is 'function' 273 | callback = includeCapabilities 274 | includeCapabilities = false 275 | 276 | includeCapabilities = includeCapabilities ? false 277 | 278 | path = @buildApiPath 'projects/' + projectId, 'includeCapabilities=' + includeCapabilities 279 | @client.get path, @getOptions(), (err, res, body) => 280 | @parseReplyData err, res, body, callback 281 | 282 | getProjectCollections: (pageSize, skip, callback) -> 283 | if typeof pageSize is 'function' 284 | callback = pageSize 285 | pageSize = skip = null 286 | else if typeof skip is 'function' 287 | callback = skip 288 | skip = null 289 | 290 | pageSize = pageSize ? 100 291 | skip = skip ? 0 292 | 293 | path = @buildApiPath 'projectcollections', '$top=' + pageSize + "&$skip=" + skip 294 | @client.get path, @getOptions(), (err, res, body) => 295 | @parseReplyData err, res, body, callback 296 | 297 | getProjectCollection: (collectionId, callback) -> 298 | 299 | path = @buildApiPath 'projectcollections/' + collectionId 300 | @client.get path, @getOptions(), (err, res, body) => 301 | @parseReplyData err, res, body, callback 302 | 303 | getTeams: (projectId, pageSize, skip, callback) -> 304 | if typeof pageSize is 'function' 305 | callback = pageSize 306 | pageSize = skip = null 307 | else if typeof skip is 'function' 308 | callback = skip 309 | skip = null 310 | 311 | pageSize = pageSize ? 100 312 | skip = skip ? 0 313 | 314 | path = @buildApiPath 'projects/' + projectId + '/teams', '$top=' + pageSize + '&$skip=' + skip 315 | @client.get path, @getOptions(), (err, res, body) => 316 | @parseReplyData err, res, body, callback 317 | 318 | getTeam: (projectId, teamId, callback) -> 319 | path = @buildApiPath 'projects/' + projectId + '/teams/' + teamId 320 | @client.get path, @getOptions(), (err, res, body) => 321 | @parseReplyData err, res, body, callback 322 | 323 | getTeamMembers: (projectId, teamId, pageSize, skip, callback) -> 324 | if typeof pageSize is 'function' 325 | callback = pageSize 326 | pageSize = skip = null 327 | else if typeof skip is 'function' 328 | callback = skip 329 | skip = null 330 | 331 | pageSize = pageSize ? 100 332 | skip = skip ? 0 333 | path = @buildApiPath 'projects/' + projectId + '/teams/' + teamId + '/members', '$top=' + pageSize + '&$skip=' + skip 334 | @client.get path, @getOptions(), (err, res, body) => 335 | @parseReplyData err, res, body, callback 336 | 337 | ######################################### 338 | # Tags 339 | ######################################### 340 | 341 | getTags: (scope, includeInactive, callback) -> 342 | if typeof includeInactive is 'function' 343 | callback = includeInactive 344 | includeInactive = false 345 | 346 | path = @buildApiPath 'tagging/scopes/' + scope + '/tags', 'includeinactive=' + includeInactive 347 | @client.get path, @getOptions(), (err, res, body) => 348 | @parseReplyData err, res, body, callback 349 | 350 | getTag: (scope, tag, callback) -> 351 | tagId = encodeURI tag 352 | path = @buildApiPath 'tagging/scopes/' + scope + '/tags/' + tagId 353 | @client.get path, @getOptions(), (err, res, body) => 354 | @parseReplyData err, res, body, callback 355 | 356 | createTag: (scope, name, callback) -> 357 | tag = 358 | name: name 359 | path = @buildApiPath 'tagging/scopes/' + scope + '/tags' 360 | @client.post path, tag, @getOptions(), (err, res, body) => 361 | @parseReplyData err, res, body, callback 362 | 363 | updateTag: (scope, tagId, name, active, callback) -> 364 | tag = 365 | name: name 366 | active: active 367 | path = @buildApiPath 'tagging/scopes/' + scope + '/tags/' + tagId 368 | @client.patch path, tag, @getOptions(), (err, res, body) => 369 | @parseReplyData err, res, body, callback 370 | 371 | deleteTag: (scope, tag, callback) -> 372 | tagId = encodeURI tag 373 | path = @buildApiPath 'tagging/scopes/' + scope + '/tags/' + tagId 374 | @client.del path, @getOptions(), (err, res, body) => 375 | @parseReplyData err, res, body, callback 376 | 377 | ######################################### 378 | # Work Items 379 | ######################################### 380 | 381 | getWorkItemIds: (wiql, projectName, callback) -> 382 | if typeof projectName is 'function' 383 | callback = projectName 384 | projectName = null 385 | 386 | # console.log query 387 | 388 | params = null 389 | 390 | if @apiVersion == "1.0-preview.1" 391 | query = 392 | wiql: wiql 393 | if projectName 394 | projectName = encodeURI projectName 395 | params = '@project=' + projectName 396 | path = @buildApiPath 'wit/queryresults', params 397 | else 398 | query = 399 | query: wiql 400 | if projectName 401 | path = @buildApiPath 'wit/wiql', params, { projectName : projectName } 402 | else 403 | path = @buildApiPath 'wit/wiql', params 404 | 405 | # console.log path, query 406 | 407 | @client.post path, query, @getOptions(), (err, res, body) => 408 | @parseReplyData err, res, body, (err, results) -> 409 | # console.log "Results", results 410 | if err 411 | callback err, results 412 | else 413 | if results && results.results 414 | ids = _.map results.results, 'sourceId' 415 | else 416 | ids = _.map results.workItems, 'id' 417 | callback err, ids 418 | 419 | getWorkItemIdsByQuery: (queryId, projectName, callback) -> 420 | if typeof projectName is 'function' 421 | callback = projectName 422 | projectName = null 423 | 424 | query = 425 | id: queryId 426 | 427 | # console.log query 428 | 429 | params = null 430 | if projectName 431 | projectName = encodeURI projectName 432 | params = '@project=' + projectName 433 | 434 | path = @buildApiPath 'wit/queryresults', params 435 | 436 | @client.post path, query, @getOptions(), (err, res, body) => 437 | @parseReplyData err, res, body, (err, results) -> 438 | if err 439 | callback err, results 440 | else 441 | # console.log results 442 | ids = _.map results.results, 'sourceId' 443 | callback err, ids 444 | 445 | getWorkItemsById: (ids, fields, asOf, expand, callback) -> 446 | if typeof fields is 'function' 447 | callback = fields 448 | fields = asOf = expand = null 449 | else if typeof asOf is 'function' 450 | callback = asOf 451 | asOf = expand = null 452 | else if typeof expand is 'function' 453 | callback = expand 454 | expand = null 455 | 456 | if typeof ids is 'Array' 457 | ids = ids.join(',') 458 | if typeof fields is 'Array' 459 | fields = fields.join(',') 460 | 461 | params = 'ids=' + ids 462 | if fields 463 | params += '&fields=' + fields 464 | 465 | if asOf 466 | params += '&asof=' + asOf 467 | 468 | if expand 469 | params += '&$expand=' + expand 470 | 471 | path = @buildApiPath 'wit/workitems', params 472 | 473 | @client.get path, @getOptions(), (err, res, body) => 474 | @parseReplyData err, res, body, callback 475 | 476 | getWorkItem: (id, expand, callback) -> 477 | if typeof expand is 'function' 478 | callback = expand 479 | expand = null 480 | 481 | params = null 482 | if expand 483 | params = '$expand=' + expand 484 | 485 | path = @buildApiPath 'wit/workitems/' + id, params 486 | @client.get path, @getOptions(), (err, res, body) => 487 | @parseReplyData err, res, body, callback 488 | 489 | createWorkItem: (item, projectName, workItemType, callback) -> 490 | if @apiVersion == "1.0-preview.1" 491 | callback = projectName unless callback? 492 | path = @buildApiPath 'wit/workitems' 493 | @client.post path, item, @getOptions(), (err, res, body) => 494 | if err 495 | callback err, body 496 | else if res.statusCode == 400 497 | callback body.exception?.Message or "Error Creating work item", body 498 | else 499 | @parseReplyData err, res, body, callback 500 | else # 1.0-preview.2 or greater 501 | path = @buildApiPath "wit/workitems/$#{workItemType}", null, { projectName : projectName } 502 | 503 | @client.patch path, item, @getOptions(true), (err, res, body) => 504 | if err 505 | callback err, body 506 | else if res.statusCode == 404 507 | callback body?.message || "Error Creating work item", body 508 | else 509 | @parseReplyData err, res, body, callback 510 | 511 | updateWorkItem: (id, operations, callback) -> 512 | path = @buildApiPath 'wit/workitems/' + id 513 | 514 | @client.patch path, operations, @getOptions(true), (err, res, body) => 515 | if res?.statusCode == 404 516 | callback body?.message || "Error Creating work item", body 517 | else 518 | @parseReplyData err, res, body, callback 519 | 520 | updateWorkItems: (items, callback) -> 521 | path = @buildApiPath 'wit/workitems' 522 | 523 | @client.patch path, items, @getOptions(true), (err, res, body) => 524 | if res?.statusCode == 404 525 | callback body?.message || "Error Creating work item", body 526 | else 527 | @parseReplyData err, res, body, callback 528 | 529 | getWorkItemUpdates: (id, pageSize, skip, callback) -> 530 | if typeof pageSize is 'function' 531 | callback = pageSize 532 | pageSize = skip = null 533 | else if typeof skip is 'function' 534 | callback = skip 535 | skip = null 536 | 537 | pageSize = pageSize ? 100 538 | skip = skip ? 0 539 | 540 | path = @buildApiPath 'wit/workitems/' + id + '/updates', '$top=' + pageSize + '&$skip=' + skip 541 | @client.get path, @getOptions(), (err, res, body) => 542 | @parseReplyData err, res, body, callback 543 | 544 | getWorkItemUpdate: (id, rev, callback) -> 545 | path = @buildApiPath 'wit/workitems/' + id + '/updates/' + rev 546 | @client.get path, @getOptions(), (err, res, body) => 547 | @parseReplyData err, res, body, callback 548 | 549 | getWorkItemRevision: (id, rev, callback) -> 550 | path = @buildApiPath 'wit/workitems/' + id + '/revisions/' + rev 551 | @client.get path, @getOptions(), (err, res, body) => 552 | @parseReplyData err, res, body, callback 553 | 554 | uploadAttachment: (project, areaPath, fileName, file, callback) -> 555 | #For binary file, use base64 encoded string 556 | params = 'project=' + encodeURI project 557 | params += '&area=' + encodeURI areaPath 558 | params += '&filename=' + encodeURI fileName 559 | path = @buildApiPath 'wit/attachments', params 560 | @client.post path, file, @getOptions(), (err, res, body) => 561 | @parseReplyData err, res, body, callback 562 | 563 | addAttachmentToWorkItem: (id, rev, fileName, locationId, comment, callback) -> 564 | item = 565 | id: id 566 | rev: rev 567 | resourceLinks: [ 568 | { 569 | type: 'attachment' 570 | name: fileName 571 | location: locationId 572 | comment: comment 573 | } 574 | ] 575 | path = @buildApiPath 'wit/workitems/' + id 576 | @client.patch path, item, @getOptions(true), (err, res, body) => 577 | @parseReplyData err, res, body, callback 578 | 579 | ######################################### 580 | # Work Item Queries 581 | ######################################### 582 | 583 | getQueries: (projectName, depth, expand, folderPath, includeDeleted, callback) -> 584 | if typeof depth is 'function' 585 | callback = depth 586 | depth = expand = folderPath = null 587 | includeDeleted = false 588 | else if typeof expand is 'function' 589 | callback = expand 590 | expand = folderPath = null 591 | includeDeleted = false 592 | else if typeof folderPath is 'function' 593 | callback = folderPath 594 | folderPath = null 595 | includeDeleted = false 596 | else if typeof includeDeleted is 'function' 597 | callback = includeDeleted 598 | includeDeleted = false 599 | 600 | folderPathParam = "" 601 | if @apiVersion == '1.0-preview.1' 602 | params = '&project=' + projectName 603 | else 604 | folderPathParam = @encodeFolderPath folderPath 605 | 606 | if depth 607 | params += '&$depth=' + depth 608 | if expand 609 | params += '&$expand=' + expand 610 | 611 | if @apiVersion == '1.0-preview.1' 612 | path = @buildApiPath 'wit/queries', params 613 | else 614 | if includeDeleted 615 | params = '&$includeDeleted=' + includeDeleted 616 | 617 | path = @buildApiPath 'wit/queries' + folderPathParam, params, { projectName : projectName } 618 | 619 | @client.get path, @getOptions(), (err, res, body) => 620 | @parseReplyData err, res, body, callback 621 | 622 | getQuery: (projectName, queryOrFolderId, folderPath, callback) -> 623 | if typeof folderPath is 'function' 624 | callback = folderPath 625 | folderPath = null 626 | 627 | if @apiVersion == '1.0-preview.1' 628 | path = @buildApiPath 'wit/queries/' + queryOrFolderId 629 | else 630 | folderPathParam = @encodeFolderPath folderPath 631 | path = @buildApiPath 'wit/queries' + folderPathParam + '/' + queryOrFolderId, null, { projectName : projectName } 632 | 633 | @client.get path, @getOptions(), (err, res, body) => 634 | @parseReplyData err, res, body, callback 635 | 636 | createQuery: (projectName, name, folderIdOrPath, wiql, callback) -> 637 | if @apiVersion == '1.0-preview.1' 638 | query = 639 | name: name 640 | parentId: folderIdOrPath 641 | wiql: wiql 642 | path = @buildApiPath 'wit/queries' 643 | else 644 | path = @buildApiPath 'wit/queries' + (@encodeFolderPath folderIdOrPath) , null, { projectName : projectName } 645 | query = 646 | name: name 647 | wiql: wiql 648 | 649 | @client.post path, query, @getOptions(), (err, res, body) => 650 | @parseReplyData err, res, body, callback 651 | 652 | updateQuery: (projectName, queryIdOrName, name, folderIdOrPath, wiql, callback) -> 653 | if @apiVersion == '1.0-preview.1' 654 | path = @buildApiPath 'wit/queries/' + queryId 655 | query = 656 | id: queryIdOrName 657 | name: name 658 | parentId: folderIdOrPath 659 | wiql: wiql 660 | path = @buildApiPath 'wit/queries' 661 | else 662 | path = if @isGuid queryIdOrName then @buildApiPath 'wit/queries/' + queryIdOrName , null, { projectName : projectName } else @buildApiPath 'wit/queries' + (@encodeFolderPath folderIdOrPath) + '/' + queryIdOrName , null, { projectName : projectName } 663 | # path = @buildApiPath 'wit/queries' + (@encodeFolderPath folderIdOrPath) + '/' + queryIdOrName , null, { projectName : projectName } 664 | query = 665 | name: name 666 | wiql: wiql 667 | 668 | @client.patch path, query, @getOptions(), (err, res, body) => 669 | @parseReplyData err, res, body, callback 670 | 671 | createFolder: (projectName, name, parentFolderIdOrPath, callback) -> 672 | if @apiVersion == '1.0-preview.1' 673 | folder = 674 | name: name 675 | parentId: parentFolderIdOrPath 676 | type: "folder" 677 | path = @buildApiPath 'wit/queries' 678 | else 679 | path = @buildApiPath 'wit/queries' + (@encodeFolderPath parentFolderIdOrPath) , null, { projectName : projectName } 680 | folder = 681 | name: name 682 | isFolder: "true" 683 | 684 | @client.post path, folder, @getOptions(), (err, res, body) => 685 | @parseReplyData err, res, body, callback 686 | 687 | deleteQuery: (projectName, queryIdOrPath, callback) -> 688 | if @apiVersion == '1.0-preview.1' 689 | path = @buildApiPath 'wit/queries/' + queryIdOrPath 690 | else 691 | path = @buildApiPath 'wit/queries' + (@encodeFolderPath queryIdOrPath) , null, { projectName : projectName } 692 | 693 | @client.del path, @getOptions(), (err, res, body) => 694 | @parseReplyData err, res, body, callback 695 | 696 | deleteFolder: (projectName, queryIdOrPath, callback) -> 697 | @deleteQuery projectName, queryIdOrPath, callback 698 | 699 | ######################################### 700 | # Work Items V2 only 701 | ######################################### 702 | 703 | getWorkItemTypes: (projectName, callback) -> 704 | @checkAndRequireMinimumVersion "1.0-preview.2" 705 | 706 | path = @buildApiPath 'wit/workitemtypes', null, { projectName : projectName } 707 | @client.get path, @getOptions(), (err, res, body) => 708 | @parseReplyData err, res, body, callback 709 | 710 | getWorkItemType: (projectName, workItemType, callback) -> 711 | @checkAndRequireMinimumVersion "1.0-preview.2" 712 | 713 | path = @buildApiPath 'wit/workitemtypes/' + workItemType, null, { projectName : projectName } 714 | @client.get path, @getOptions(), (err, res, body) => 715 | @parseReplyData err, res, body, callback 716 | 717 | getWorkItemRelationTypes: (callback) -> 718 | @checkAndRequireMinimumVersion "1.0-preview.2" 719 | 720 | path = @buildApiPath 'wit/workitemrelationtypes' 721 | @client.get path, @getOptions(), (err, res, body) => 722 | @parseReplyData err, res, body, callback 723 | 724 | getWorkItemRelationType: (relationName, callback) -> 725 | @checkAndRequireMinimumVersion "1.0-preview.2" 726 | 727 | path = @buildApiPath 'wit/workitemrelationtypes/' + relationName 728 | @client.get path, @getOptions(), (err, res, body) => 729 | @parseReplyData err, res, body, callback 730 | 731 | getWorkItemCategories: (projectName, callback) -> 732 | @checkAndRequireMinimumVersion "1.0-preview.2" 733 | 734 | path = @buildApiPath 'wit/workitemtypecategories', null, { projectName : projectName } 735 | @client.get path, @getOptions(), (err, res, body) => 736 | @parseReplyData err, res, body, callback 737 | 738 | getWorkItemCategory: (projectName, categoryName, callback) -> 739 | @checkAndRequireMinimumVersion "1.0-preview.2" 740 | 741 | path = @buildApiPath 'wit/workitemtypecategories/' + categoryName, null, { projectName : projectName } 742 | @client.get path, @getOptions(), (err, res, body) => 743 | @parseReplyData err, res, body, callback 744 | 745 | getWorkItemFields: (callback) -> 746 | @checkAndRequireMinimumVersion "1.0-preview.2" 747 | 748 | path = @buildApiPath 'wit/fields' 749 | @client.get path, @getOptions(), (err, res, body) => 750 | @parseReplyData err, res, body, callback 751 | 752 | getWorkItemField: (referenceName, callback) -> 753 | @checkAndRequireMinimumVersion "1.0-preview.2" 754 | 755 | path = @buildApiPath 'wit/fields/' + referenceName 756 | @client.get path, @getOptions(), (err, res, body) => 757 | @parseReplyData err, res, body, callback 758 | 759 | 760 | ######################################### 761 | # Accounts and Profiles 762 | ######################################### 763 | 764 | getAccounts: (memberId, callback) -> 765 | @checkAndRequireOAuth 'getAccounts' 766 | path = @buildApiPath 'accounts', 'memberid=' + memberId, { excludeCollection : true } 767 | @clientSPS.get path, @getOptions(), (err, res, body) => 768 | @parseReplyData err, res, body, callback 769 | 770 | 771 | getCurrentProfile: (callback) -> 772 | @checkAndRequireOAuth "getCurrentProfile" 773 | path = @buildApiPath 'profile/profiles/me', null, {excludeCollection : true } 774 | @clientSPS.get path, @getOptions(), (err, res, body) => 775 | @parseReplyData err, res, body, callback 776 | 777 | getConnectionData: (callback) -> 778 | path = @buildApiPath 'connectionData' 779 | @client.get path, @getOptions(), (err, res, body) => 780 | @parseReplyData err, res, body, callback 781 | 782 | 783 | ######################################### 784 | # Team Rooms 785 | ######################################### 786 | 787 | getRooms: (callback) -> 788 | path = @buildApiPath 'chat/rooms' 789 | @client.get path, @getOptions(), (err, res, body) => 790 | @parseReplyData err, res, body, callback 791 | 792 | getRoom: (roomId, callback) -> 793 | path = @buildApiPath 'chat/rooms/' + roomId 794 | @client.get path, @getOptions(), (err, res, body) => 795 | @parseReplyData err, res, body, callback 796 | 797 | createRoom: (name, description, callback) -> 798 | path = @buildApiPath 'chat/rooms' 799 | room = 800 | name: name 801 | description: description 802 | @client.post path, room, @getOptions(), (err, res, body) => 803 | @parseReplyData err, res, body, callback 804 | 805 | updateRoom: (roomId, name, description, callback) -> 806 | path = @buildApiPath 'chat/rooms/' + roomId 807 | room = 808 | name: name 809 | description: description 810 | @client.patch path, room, @getOptions(), (err, res, body) => 811 | @parseReplyData err, res, body, callback 812 | 813 | deleteRoom: (roomId, callback) -> 814 | path = @buildApiPath 'chat/rooms/' + roomId 815 | @client.del path, @getOptions(), (err, res, body) => 816 | @parseReplyData err, res, body, callback 817 | 818 | getRoomUsers: (roomId, callback) -> 819 | path = @buildApiPath 'chat/rooms/' + roomId + '/users' 820 | @client.get path, @getOptions(), (err, res, body) => 821 | @parseReplyData err, res, body, callback 822 | 823 | getRoomUser: (roomId, userId, callback) -> 824 | path = @buildApiPath 'chat/rooms/' + roomId + '/users/' + userId 825 | @client.get path, @getOptions(), (err, res, body) => 826 | @parseReplyData err, res, body, callback 827 | 828 | joinRoom: (roomId, userId, userGuid, callback) -> 829 | # console.log userId 830 | path = @buildApiPath 'chat/rooms/' + roomId + '/users/' + userGuid 831 | @client.put path, userId, @getOptions(), (err, res, body) -> 832 | callback err, res.statusCode 833 | 834 | leaveRoom: (roomId, userId, callback) -> 835 | path = @buildApiPath 'chat/rooms/' + roomId + '/users/' + userId 836 | @client.del path, @getOptions(), (err, res, body) => 837 | @parseReplyData err, res, body, callback 838 | 839 | getMessages: (roomId, startDate, endDate, callback) -> 840 | params = null 841 | if typeof startDate is 'function' 842 | callback = startDate 843 | else if startDate or endDate 844 | params = '$filter=' 845 | if typeof endDate is 'function' 846 | callback = endDate 847 | endDate = null 848 | if startDate and endDate 849 | params += 'postedtime ge ' + startDate + ' and postedtime lt ' + endDate 850 | else if startDate 851 | params += 'postedtime ge ' + startDate 852 | else 853 | params += 'postedtime lt ' + endDate 854 | 855 | path = @buildApiPath 'chat/rooms/' + roomId + '/messages', params 856 | @client.get path, @getOptions(), (err, res, body) => 857 | @parseReplyData err, res, body, callback 858 | 859 | getMessage: (roomId, messageId, callback) -> 860 | path = @buildApiPath 'chat/rooms/' + roomId + '/messages/' + messageId 861 | @client.get path, @getOptions(), (err, res, body) => 862 | @parseReplyData err, res, body, callback 863 | 864 | createMessage: (roomId, message, callback) -> 865 | path = @buildApiPath 'chat/rooms/' + roomId + '/messages' 866 | @client.post path, message, @getOptions(), (err, res, body) => 867 | @parseReplyData err, res, body, callback 868 | 869 | updateMessage: (roomId, messageId, message, callback) -> 870 | path = @buildApiPath 'chat/rooms/' + roomId + '/messages/' + messageId 871 | @client.patch path, null, @getOptions(), (err, res, body) => 872 | # console.log res 873 | @parseReplyData err, res, body, callback 874 | 875 | deleteMessage: (roomId, messageId, callback) -> 876 | path = @buildApiPath 'chat/rooms/' + roomId + '/messages/' + messageId 877 | @client.del path, @getOptions(), (err, res, body) => 878 | @parseReplyData err, res, body, callback 879 | 880 | ######################################### 881 | # Version Control 882 | ######################################### 883 | 884 | getRootBranches: (includeChildren, includeDeleted, callback) -> 885 | if typeof includeChildren is 'function' 886 | callback = includeChildren 887 | includeChildren = includeDeleted = false 888 | if typeof includeDeleted is 'function' 889 | callback = includeDeleted 890 | includeDeleted = false 891 | 892 | params = [] 893 | if includeChildren 894 | params.push "includechildren=true" 895 | if includeDeleted 896 | params.push "includedeleted=true" 897 | p = params.join '&' 898 | path = @buildApiPath 'tfvc/branches', p 899 | # console.log path 900 | @client.get path, @getOptions(), (err, res, body) => 901 | @parseReplyData err, res, body, callback 902 | 903 | getBranch: (path, includeChildren, includeParent, includeDeleted, callback) -> 904 | if typeof includeChildren is 'function' 905 | callback = includeChildren 906 | includeChildren = includeParent = includeDeleted = false 907 | if typeof includeParent is 'function' 908 | callback = includeParent 909 | includeParent = includeDeleted = false 910 | if typeof includeDeleted is 'function' 911 | callback = includeDeleted 912 | includeDeleted = false 913 | 914 | params = [] 915 | if includeChildren 916 | params.push "includechildren=true" 917 | if includeParent 918 | params.push "includeparent=true" 919 | if includeDeleted 920 | params.push "includedeleted=true" 921 | p = params.join '&' 922 | path = @buildApiPath 'tfvc/branches/' + path, p 923 | # console.log path 924 | @client.get path, @getOptions(), (err, res, body) => 925 | @parseReplyData err, res, body, callback 926 | 927 | getShelveSets: (owner, maxCommentLength, pageSize, skip, callback) -> 928 | if typeof owner is 'function' 929 | callback = owner 930 | owner = maxCommentLength = pageSize = skip = null 931 | if typeof maxCommentLength is 'function' 932 | callback = maxCommentLength 933 | maxCommentLength = pageSize = skip = null 934 | if typeof pageSize is 'function' 935 | callback = pageSize 936 | pageSize = skip = null 937 | if typeof skip is 'function' 938 | callback = skip 939 | skip = null 940 | 941 | maxCommentLength = maxCommentLength ? 80 942 | pageSize = pageSize ? 100 943 | skip = skip ? 0 944 | params = [] 945 | if owner 946 | params.push 'owner=' + owner 947 | params.push 'maxcommentlength=' + maxCommentLength 948 | params.push '$top=' + pageSize 949 | params.push '$skip=' + skip 950 | p = params.join '&' 951 | path = @buildApiPath 'tfvc/shelvesets', p 952 | @client.get path, @getOptions(), (err, res, body) => 953 | @parseReplyData err, res, body, callback 954 | 955 | getChangeSets: (queryOptions, callback) -> 956 | if typeof queryOptions is 'function' 957 | callback = queryOptions 958 | queryOptions = null 959 | 960 | params = [] 961 | if queryOptions 962 | if queryOptions.itemPath 963 | params.push 'itempath=' + queryOptions.itemPath 964 | if queryOptions.version 965 | params.push 'version=' + queryOptions.version 966 | if queryOptions.versionType 967 | params.push 'versiontype=' + queryOptions.versionType 968 | if queryOptions.versionOption 969 | params.push 'versionoption=' + queryOptions.versionOption 970 | if queryOptions.author 971 | params.push 'author=' + queryOptions.author 972 | if queryOptions.fromId 973 | params.push 'fromId=' + queryOptions.fromId 974 | if queryOptions.toId 975 | params.push 'toId=' + queryOptions.toId 976 | if queryOptions.fromDate 977 | params.push 'fromDate=' + queryOptions.fromDate 978 | if queryOptions.toDate 979 | params.push 'toDate=' + queryOptions.toDate 980 | if queryOptions.pageSize 981 | params.push '$top=' + queryOptions.pageSize 982 | if queryOptions.skip 983 | params.push '$skip=' + queryOptions.skip 984 | if queryOptions.orderby 985 | params.push '$orderby=' + queryOptions.orderby 986 | if queryOptions.maxCommentLength 987 | params.push 'maxcommentlength=' + queryOptions.maxCommentLength 988 | 989 | p = params.join '&' 990 | path = @buildApiPath 'tfvc/changesets', p 991 | @client.get path, @getOptions(), (err, res, body) => 992 | @parseReplyData err, res, body, callback 993 | 994 | getChangeSet: (changesetId, queryOptions, callback) -> 995 | if typeof queryOptions is 'function' 996 | callback = queryOptions 997 | queryOptions = null 998 | 999 | # console.log 'queryOptions', queryOptions 1000 | params = [] 1001 | if queryOptions 1002 | if queryOptions.includeDetails 1003 | params.push 'includedetails=true' 1004 | if queryOptions.includeWorkItems 1005 | params.push 'includeworkitems=true' 1006 | if queryOptions.maxChangeCount 1007 | params.push 'maxchangecount=' + queryOptions.maxChangeCount 1008 | if queryOptions.maxCommentLength 1009 | params.push 'maxcommentlength=' + queryOptions.maxCommentLength 1010 | 1011 | p = params.join '&' 1012 | path = @buildApiPath 'tfvc/changesets/' + changesetId, p 1013 | # console.log path 1014 | @client.get path, @getOptions(), (err, res, body) => 1015 | @parseReplyData err, res, body, callback 1016 | 1017 | getChangeSetChanges: (queryOptions, callback) -> 1018 | if typeof queryOptions is 'function' 1019 | callback = queryOptions 1020 | queryOptions = null 1021 | url = 'tfvc/changesets/latest/changes' 1022 | params = [] 1023 | if queryOptions 1024 | if queryOptions.id 1025 | url = 'tfvc/changesets/' + queryOptions.id + '/changes' 1026 | if queryOptions.pageSize 1027 | params.push '$top=' + queryOptions.pageSize 1028 | if queryOptions.skip 1029 | params.push '$skip=' + queryOptions.skip 1030 | p = params.join '&' 1031 | path = @buildApiPath url, p 1032 | # console.log path 1033 | @client.get path, @getOptions(), (err, res, body) => 1034 | @parseReplyData err, res, body, callback 1035 | 1036 | getChangeSetWorkItems: (queryOptions, callback) -> 1037 | if typeof queryOptions is 'function' 1038 | callback = queryOptions 1039 | queryOptions = null 1040 | url = 'tfvc/changesets/latest/workitems' 1041 | params = [] 1042 | if queryOptions 1043 | if queryOptions.id 1044 | url = 'tfvc/changesets/' + queryOptions.id + '/workitems' 1045 | if queryOptions.pageSize 1046 | params.push '$top=' + queryOptions.pageSize 1047 | if queryOptions.skip 1048 | params.push '$skip=' + queryOptions.skip 1049 | p = params.join '&' 1050 | path = @buildApiPath url, p 1051 | # console.log path 1052 | @client.get path, @getOptions(), (err, res, body) => 1053 | @parseReplyData err, res, body, callback 1054 | 1055 | getLabels: (queryOptions, callback) -> 1056 | if typeof queryOptions is 'function' 1057 | callback = queryOptions 1058 | queryOptions = null 1059 | 1060 | params = [] 1061 | if queryOptions 1062 | if queryOptions.name 1063 | params.push 'name=' + queryOptions.name 1064 | if queryOptions.owner 1065 | params.push 'owner=' + queryOptions.owner 1066 | if queryOptions.itemLabelFilter 1067 | params.push 'itemlabelfilter=' + queryOptions.itemLabelFilter 1068 | if queryOptions.pageSize 1069 | params.push '$top=' + queryOptions.pageSize 1070 | if queryOptions.skip 1071 | params.push '$skip=' + queryOptions.skip 1072 | 1073 | p = params.join '&' 1074 | path = @buildApiPath 'tfvc/labels', p 1075 | # console.log path 1076 | @client.get path, @getOptions(), (err, res, body) => 1077 | @parseReplyData err, res, body, callback 1078 | 1079 | getLabel: (labelId, maxItemCount, callback) -> 1080 | if typeof maxItemCount is 'function' 1081 | callback = maxItemCount 1082 | maxItemCount = null 1083 | params = 'maxitemcount=' + maxItemCount ? '' 1084 | path = @buildApiPath 'tfvc/labels/' + labelId, params 1085 | # console.log path 1086 | @client.get path, @getOptions(), (err, res, body) => 1087 | @parseReplyData err, res, body, callback 1088 | 1089 | getItemsByLabel: (labelId, pageSize, skip, callback) -> 1090 | if typeof pageSize is 'function' 1091 | callback = pageSize 1092 | pageSize = skip = null 1093 | if typeof skip is 'function' 1094 | callback = skip 1095 | skip = null 1096 | 1097 | pageSize = pageSize ? 100 1098 | skip = skip ? 0 1099 | 1100 | params = '$top=' + pageSize + '&$skip=' + skip 1101 | path = @buildApiPath 'tfvc/labels/' + labelId + '/items', params 1102 | # console.log path 1103 | @client.get path, @getOptions(), (err, res, body) => 1104 | @parseReplyData err, res, body, callback 1105 | 1106 | ######################################### 1107 | # Git Repositories 1108 | ######################################### 1109 | 1110 | getRepositories: (projectId, callback) -> 1111 | path = '' 1112 | if typeof projectId is 'function' 1113 | callback = projectId 1114 | projectId = null 1115 | if projectId 1116 | path = @buildApiPath 'git/' + projectId + '/repositories' 1117 | else 1118 | path = @buildApiPath 'git/repositories' 1119 | @client.get path, @getOptions(), (err, res, body) => 1120 | @parseReplyData err, res, body, callback 1121 | 1122 | getRepository: (repositoryIdOrName, projectId, callback) -> 1123 | path = '' 1124 | if typeof projectId is 'function' 1125 | callback = projectId 1126 | projectId = null 1127 | repo = encodeURI repositoryIdOrName 1128 | if projectId 1129 | path = @buildApiPath 'git/' + projectId + '/repositories/' + repo 1130 | else 1131 | path = @buildApiPath 'git/repositories/' + repo 1132 | @client.get path, @getOptions(), (err, res, body) => 1133 | @parseReplyData err, res, body, callback 1134 | 1135 | createRepository: (projectId, name, callback) -> 1136 | repo = 1137 | name: name 1138 | project: 1139 | id: projectId 1140 | path = @buildApiPath 'git/repositories' 1141 | @client.post path, repo, @getOptions(), (err, res, body) => 1142 | @parseReplyData err, res, body, callback 1143 | 1144 | renameRepository: (repositoryId, name, callback) -> 1145 | repo = 1146 | id: repositoryId 1147 | name: name 1148 | path = @buildApiPath 'git/repositories/' + repositoryId 1149 | @client.patch path, repo, @getOptions(), (err, res, body) => 1150 | @parseReplyData err, res, body, callback 1151 | 1152 | deleteRepository: (repositoryId, callback) -> 1153 | path = @buildApiPath 'git/repositories/' + repositoryId 1154 | @client.del path, @getOptions(), (err, res, body) => 1155 | @parseReplyData err, res, body, callback 1156 | 1157 | getCommits: (repositoryId, itemPath, committer, author, fromDate, toDate, pageSize, skip, callback) -> 1158 | if typeof itemPath is 'function' 1159 | callback = itemPath 1160 | itemPath = committer = author = fromDate = toDate = pageSize = skip = null 1161 | else if typeof committer is 'function' 1162 | callback = committer 1163 | committer = author = fromDate = toDate = pageSize = skip = null 1164 | else if typeof author is 'function' 1165 | callback = author 1166 | author = fromDate = toDate = pageSize = skip = null 1167 | else if typeof fromDate is 'function' 1168 | callback = fromDate 1169 | fromDate = toDate = pageSize = skip = null 1170 | else if typeof toDate is 'function' 1171 | callback = toDate 1172 | toDate = pageSize = skip = null 1173 | else if typeof pageSize is 'function' 1174 | callback = pageSize 1175 | pageSize = skip = null 1176 | else if typeof skip is 'function' 1177 | callback = skip 1178 | skip = null 1179 | 1180 | skip = skip ? 0 1181 | pageSize = pageSize ? 1000 1182 | 1183 | params = '$top=' + pageSize + '&$skip=' + skip 1184 | if itemPath 1185 | params += '&itempath=' + itemPath 1186 | if committer 1187 | params += '&committer=' + committer 1188 | if author 1189 | params += '&author=' + author 1190 | if fromDate 1191 | params += '&fromdate=' + fromDate 1192 | if toDate 1193 | params += '&todate=' + toDate 1194 | 1195 | path = @buildApiPath 'git/repositories/' + repositoryId + '/commits', params 1196 | # console.log path 1197 | @client.get path, @getOptions(), (err, res, body) => 1198 | @parseReplyData err, res, body, callback 1199 | 1200 | getCommit: (repositoryId, commitId, changeCount, callback) -> 1201 | if typeof changeCount is 'function' 1202 | callback = changeCount 1203 | changeCount = 0 1204 | 1205 | path = @buildApiPath 'git/repositories/' + repositoryId + '/commits/' + commitId, 'changeCount=' + changeCount 1206 | # console.log path 1207 | @client.get path, @getOptions(), (err, res, body) => 1208 | @parseReplyData err, res, body, callback 1209 | 1210 | getDiffs: (repositoryId, baseVersionType, baseVersion, targetVersionType, targetVersion, pageSize, skip, callback) -> 1211 | if typeof baseVersionType is 'function' 1212 | callback = baseVersionType 1213 | baseVersionType = baseVersion = targetVersionType = targetVersion = pageSize = skip = null 1214 | else if typeof baseVersion is 'function' 1215 | callback = baseVersion 1216 | baseVersion = targetVersionType = targetVersion = pageSize = skip = null 1217 | else if typeof targetVersionType is 'function' 1218 | callback = targetVersionType 1219 | targetVersionType = targetVersion = pageSize = skip = null 1220 | else if typeof targetVersion is 'function' 1221 | callback = targetVersion 1222 | targetVersion = pageSize = skip = null 1223 | else if typeof pageSize is 'function' 1224 | callback = pageSize 1225 | pageSize = skip = null 1226 | else if typeof skip is 'function' 1227 | callback = skip 1228 | skip = null 1229 | 1230 | skip = skip ? 0 1231 | pageSize = pageSize ? 1000 1232 | 1233 | params = '$top=' + pageSize + '&$skip=' + skip 1234 | if (baseVersionType) 1235 | params += '&baseversiontype=' + baseVersionType 1236 | if (targetVersionType) 1237 | params += '&targetversiontype=' + targetVersionType 1238 | if (baseVersion) 1239 | params += '&baseversion=' + baseVersion 1240 | if (targetVersion) 1241 | params += '&targetversion=' + targetVersion 1242 | 1243 | path = @buildApiPath 'git/repositories/' + repositoryId + '/diffs/commits', params 1244 | # console.log path 1245 | @client.get path, @getOptions(), (err, res, body) => 1246 | @parseReplyData err, res, body, callback 1247 | 1248 | getPushes: (repositoryId, fromDate, toDate, pusherId, pageSize, skip, callback) -> 1249 | if typeof fromDate is 'function' 1250 | callback = fromDate 1251 | fromDate = toDate = pusherId = pageSize = skip = null 1252 | else if typeof toDate is 'function' 1253 | callback = toDate 1254 | toDate = pusherId = pageSize = skip = null 1255 | else if typeof pusherId is 'function' 1256 | callback = pusherId 1257 | pusherId = pageSize = skip = null 1258 | else if typeof pageSize is 'function' 1259 | callback = pageSize 1260 | pageSize = skip = null 1261 | else if typeof skip is 'function' 1262 | callback = skip 1263 | skip = null 1264 | 1265 | skip = skip ? 0 1266 | pageSize = pageSize ? 1000 1267 | 1268 | params = '$top=' + pageSize + '&$skip=' + skip 1269 | if (fromDate) 1270 | params += '&fromdate=' + fromDate 1271 | if (toDate) 1272 | params += '&todate=' + toDate 1273 | if (pusherId) 1274 | params += '&pusherid=' + pusherId 1275 | 1276 | path = @buildApiPath 'git/repositories/' + repositoryId + '/pushes', params 1277 | # console.log path 1278 | @client.get path, @getOptions(), (err, res, body) => 1279 | @parseReplyData err, res, body, callback 1280 | 1281 | getStats: (repositoryId, branchName, baseVersionType, baseVersion, callback) -> 1282 | if typeof branchName is 'function' 1283 | callback = branchName 1284 | branchName = baseVersionType = baseVersion = null 1285 | else if typeof baseVersionType is 'function' 1286 | callback = baseVersionType 1287 | baseVersionType = baseVersion = null 1288 | else if typeof baseVersion is 'function' 1289 | callback = baseVersion 1290 | baseVersion = null 1291 | 1292 | params = [] 1293 | if baseVersionType 1294 | params.push 'baseversiontype=' + baseVersionType 1295 | if baseVersion 1296 | params.push 'baseversion=' + baseVersion 1297 | 1298 | url = 'git/repositories/' + repositoryId + '/stats/branches' 1299 | if branchName 1300 | url += '/' + branchName 1301 | 1302 | path = @buildApiPath url, params.join '&' 1303 | # console.log path 1304 | @client.get path, @getOptions(), (err, res, body) => 1305 | @parseReplyData err, res, body, callback 1306 | 1307 | getRefs: (repositoryId, filter, callback) -> 1308 | if typeof filter is 'function' 1309 | callback = filter 1310 | filter = null 1311 | 1312 | url = 'git/repositories/' + repositoryId + '/refs' 1313 | if filter 1314 | url += '/' + filter 1315 | 1316 | path = @buildApiPath url 1317 | @client.get path, @getOptions(), (err, res, body) => 1318 | @parseReplyData err, res, body, callback 1319 | 1320 | ######################################### 1321 | # Builds 1322 | ######################################### 1323 | 1324 | getBuildDefinitions: (callback) -> 1325 | path = @buildApiPath 'build/definitions' 1326 | @client.get path, @getOptions(), (err, res, body) => 1327 | @parseReplyData err, res, body, callback 1328 | 1329 | queueBuild: (buildRequest, callback) -> 1330 | path = @buildApiPath 'build/requests' 1331 | @client.post path, buildRequest, @getOptions(), (err, res, body) => 1332 | @parseReplyData err, res, body, callback 1333 | 1334 | 1335 | ######################################### 1336 | # Service Hooks 1337 | ######################################### 1338 | 1339 | getPublishers: (callback) -> 1340 | path = @buildApiPath 'hooks/publishers' 1341 | @client.get path, @getOptions(), (err, res, body) => 1342 | @parseReplyData err, res, body, callback 1343 | 1344 | getConsumers: (callback) -> 1345 | path = @buildApiPath 'hooks/consumers' 1346 | @client.get path, @getOptions(), (err, res, body) => 1347 | @parseReplyData err, res, body, callback 1348 | 1349 | getConsumer: (consumerId, callback) -> 1350 | path = @buildApiPath 'hooks/consumers/' + consumerId 1351 | @client.get path, @getOptions(), (err, res, body) => 1352 | @parseReplyData err, res, body, callback 1353 | 1354 | getConsumerActions: (consumerId, callback) -> 1355 | path = @buildApiPath 'hooks/consumers/' + consumerId + '/actions' 1356 | @client.get path, @getOptions(), (err, res, body) => 1357 | @parseReplyData err, res, body, callback 1358 | 1359 | getConsumerAction: (consumerId, action, callback) -> 1360 | path = @buildApiPath 'hooks/consumers/' + consumerId + '/actions/' + action 1361 | @client.get path, @getOptions(), (err, res, body) => 1362 | @parseReplyData err, res, body, callback 1363 | 1364 | getSubscriptions: (callback) -> 1365 | path = @buildApiPath 'hooks/subscriptions' 1366 | @client.get path, @getOptions(), (err, res, body) => 1367 | @parseReplyData err, res, body, callback 1368 | 1369 | createSubscription: (subscription, callback) -> 1370 | path = @buildApiPath 'hooks/subscriptions' 1371 | @client.post path, subscription, @getOptions(), (err, res, body) => 1372 | @parseReplyData err, res, body, callback 1373 | 1374 | querySubscriptions: (queryOptions, callback) -> 1375 | path = @buildApiPath 'hooks/subscriptionsquery' 1376 | @client.post path, queryOptions, @getOptions(), (err, res, body) => 1377 | @parseReplyData err, res, body, callback 1378 | 1379 | deleteSubscription: (id, callback) -> 1380 | path = @buildApiPath 'hooks/subscriptions/' + id 1381 | @client.del path, @getOptions(), (err, res, body) => 1382 | @parseReplyData err, res, body, callback 1383 | --------------------------------------------------------------------------------