├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── index.js ├── lib ├── Error.js ├── HelloSignMethod.basic.js ├── HelloSignMethod.js ├── HelloSignResource.js ├── hellosign.js ├── resources │ ├── Account.js │ ├── ApiApp.js │ ├── BulkSendJob.js │ ├── Embedded.js │ ├── Oauth.js │ ├── Reports.js │ ├── SignatureRequest.js │ ├── Team.js │ ├── Template.js │ └── UnclaimedDraft.js └── utils.js ├── package-lock.json ├── package.json ├── server.js └── test ├── _initial.js ├── hellosign-sdk-test-server ├── lib │ ├── fixtures │ │ ├── NDA.pdf │ │ └── files.zip │ ├── helpers.js │ └── v3.spec.js ├── package.json ├── server.js └── uploads │ └── .gitignore ├── mocharc.json ├── mock-functional ├── NDA.pdf ├── account.js └── signature_request.js ├── mock-tests.sh ├── test-runner.sh ├── testparams.example.js └── unit ├── endpoints.js ├── errors_and_utilities.js └── request_utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | test/testparams.js 4 | node_modules 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | - "9" 5 | - "10" 6 | - "node" 7 | - "lts/*" 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We're excited you're considering contributing to our project! Your patches are essential to keeping our SDKs bug free and up-to-date. We want to keep it as easy as possible to contribute. There are only a few guidelines you must follow that will allow us to keep up with pull requests and maintain a high quality code base. 4 | 5 | ## Getting Started 6 | 7 | * Make sure you have a [GitHub account](https://github.com/signup/free) 8 | * Make sure you have a [HelloSign account](http://www.hellosign.com) 9 | * Submit a new [issue ticket](https://github.com/HelloFax/hellosign-java-sdk/issues), assuming one does not already exist. 10 | * Clearly describe the issue including steps to reproduce when it is a bug. 11 | * Make sure to include the version you are using which has the issue (and confirm that the issue you are having has not been fixed in a later version) 12 | * Fork the repository on GitHub 13 | 14 | ## Making Changes 15 | 16 | * Make commits of logical units. 17 | * Check for unnecessary whitespace with `git diff --check` before committing. 18 | * Make sure your commit messages are in [the proper format](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). 19 | * Make sure you have added any necessary tests for your changes. 20 | * Run all unit tests, if available, to ensure nothing else was accidentally broken. 21 | 22 | ### Making trivial changes 23 | 24 | For changes of a trivial nature to comments and documentation, it is not 25 | always necessary to create a new issue ticket. 26 | 27 | ## Submitting Changes 28 | 29 | * Push your changes to your fork of the repository. 30 | * Submit a pull request to the repository in the HelloFax organization. 31 | * Update your issue ticket to mark that you have submitted code and are ready for it to be reviewed (Status: Ready for Merge). 32 | * Include a link to the pull request in the ticket. 33 | * The HelloSign dev team looks at Pull Requests on a regular basis. 34 | * After feedback has been given we expect responses within two weeks. After two 35 | weeks we may close the pull request if it isn't showing any activity. 36 | 37 | # Additional Resources 38 | 39 | * [General GitHub documentation](http://help.github.com/) 40 | * [GitHub pull request documentation](http://help.github.com/send-pull-requests/) 41 | * [HelloSign API documentation](https://www.hellosign.com/api/documentation) 42 | * [HelloSign API HipChat room](https://www.hipchat.com/gq4BMFKt1) 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (C) 2015 hellosign.com 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚠ This SDK has been deprecated ⚠ 2 | 3 | This SDK is now deprecated and will no longer receive feature updates or bug fixes. Security fixes will still be applied as needed. 4 | 5 | The new `@dropbox/sign` SDK can be found at [hellosign/dropbox-sign-node](https://github.com/hellosign/dropbox-sign-node)! 6 | 7 | The new SDK and this legacy SDK are _not_ backwards-compatible! 8 | 9 | Please [see here for a comprehensive migration guide](https://developers.hellosign.com/docs/sdks/node/migration-guide/). 10 | 11 | ---- 12 | 13 | Hellosign NodeJS SDK [![Build Status](https://travis-ci.org/HelloFax/hellosign-nodejs-sdk.svg?branch=v3)](https://travis-ci.org/HelloFax/hellosign-nodejs-sdk) 14 | ------------------- 15 | 16 | 17 | A NodeJS / JavaScript wrapper for the [HelloSign API](http://www.hellosign.com/api) 18 | 19 | - [Installation](#installation) 20 | - [Configuration](#configuration) 21 | - [Usage](#usage) 22 | - [Modules](#modules) 23 | - [Tests](#tests) 24 | - [Additional Notes](#additional-notes) 25 | 26 | 27 | ## Installation 28 | 29 | First time using npm? Read more about installing npm packages [here] (https://docs.npmjs.com/downloading-and-installing-packages-locally). 30 | 31 | Install from npm: 32 | ````sh 33 | # Optionally, to scaffold your package.json first 34 | npm init 35 | 36 | npm install hellosign-sdk 37 | # Optionally, to install testing / development dependencies 38 | cd node_modules/hellosign-sdk 39 | npm install 40 | ```` 41 | 42 | Install from code: 43 | ````sh 44 | git clone https://github.com/HelloFax/hellosign-nodejs-sdk.git 45 | cd hellosign-nodejs-sdk 46 | # install dependencies 47 | npm install 48 | ```` 49 | 50 | ## Configuration 51 | 52 | In your Node application, require `hellosign-sdk` (or the path to the sdk folder if not using npm) and pass authentication information to initialize it: 53 | 54 | ````javascript 55 | // Initialize using api key 56 | var hellosign = require('hellosign-sdk')({key: 'YOUR API KEY HERE'}); 57 | 58 | OR 59 | 60 | // Initialize using email and password 61 | var hellosign = require('hellosign-sdk')({username: 'your_email_address', password: 'your_password'}); 62 | 63 | OR 64 | 65 | // Initialize for embedded requests using your api key, client id, and (optionally, for OAuth) client secret 66 | var hellosign = require('hellosign-sdk')({key: 'YOUR API KEY HERE', client_id: 'your client id', client_secret: 'your client secret'}); 67 | 68 | ```` 69 | 70 | For initialization for Oauth app-specific calls, see the [Oauth section below](#oauth). 71 | 72 | ## Usage 73 | 74 | Each function in the SDK is called from your initialized hellosign object, followed by the module name, and then the method. 75 | 76 | ```javascript 77 | hellosign.template.list(); 78 | ``` 79 | 80 | See [below](#modules) for a list of modules and their associated endpoint methods. 81 | 82 | ### Return values 83 | 84 | The results of each method can be accessed either as a callback, or a promise: 85 | 86 | Callback style responses are included as the last (or only, in the case of no others) parameter in a call: 87 | 88 | ```javascript 89 | hellosign.signatureRequest.send({/*options*/}, function(err, response){ 90 | if (err) { 91 | //do something with error 92 | } else { 93 | //parse response 94 | } 95 | }); 96 | ``` 97 | 98 | Promise style access is through Javascript! 99 | 100 | ```javascript 101 | hellosign.signatureRequest.send({/*options*/}) 102 | .then(function(response){ 103 | //parse response 104 | }) 105 | .catch(function(err){ 106 | //do something with error 107 | }) 108 | .finally(function(){ 109 | //optionally do yet another thing 110 | }); 111 | ``` 112 | 113 | Returned promises are then-able, or can be returned for later resolution. 114 | 115 | 116 | ## Modules 117 | 118 | Modules in the SDK are as follows: 119 | 120 | - [Account](#account) 121 | - [Signature Request](#signature-request) 122 | - [Embedded](#embedded) 123 | - [OAuth](#oauth) 124 | - [Team](#team) 125 | - [Template](#template) 126 | - [Reports](#reports) 127 | - [Unclaimed Draft](#unclaimed-draft) 128 | - [API App](#api-app) 129 | 130 | ### Account 131 | 132 | #### Get current account information 133 | 134 | ````javascript 135 | hellosign.account.get() 136 | .then(); 137 | ```` 138 | 139 | #### Update your account information 140 | 141 | ````javascript 142 | var new_callback_url = "https://www.example.com/callback" 143 | hellosign.account.update({callback_url: new_callback_url}) 144 | .then(); 145 | ```` 146 | 147 | #### Create a new HelloSign account 148 | 149 | ````javascript 150 | var email: "new_user@example.com"; 151 | 152 | hellosign.account.create({ 153 | email_address: email 154 | }) 155 | .then(); 156 | ```` 157 | 158 | #### Verify if an account exists (only for paid API users) 159 | 160 | ````javascript 161 | var email: "possibly_existing_user@example.com"; 162 | var account_id: '12738igfe87egqo22'; 163 | 164 | hellosign.account.verify({email_address: email,}) 165 | .then(); 166 | 167 | hellosign.account.verify({account_id: email,}) 168 | .then(); 169 | ```` 170 | 171 | ### Signature Request 172 | 173 | 174 | #### Get an existing Signature Request by ID 175 | 176 | ````javascript 177 | hellosign.signatureRequest.get("fa5c8a0b0f492d768749333ad6fcc214c111e967") 178 | .then(); 179 | ```` 180 | 181 | #### Get a list of your Signature Requests 182 | 183 | ````javascript 184 | hellosign.signatureRequest.list() 185 | .then(function(res){ 186 | console.log(res.signature_requests); 187 | }); 188 | ```` 189 | 190 | This endpoint can optionally receive the parameters `page`, and `page_size`, passed in as an options object: 191 | 192 | ````javascript 193 | hellosign.signatureRequest.list({page: 2, page_size: 15}) 194 | ```` 195 | 196 | This endpoint can optionally be used to query and search for items such as `page`, `title`, and `from` passed in as an options object: 197 | 198 | ````javascript 199 | const opts = { 200 | page: 1, 201 | query: 'title:Test +title AND from:me' 202 | } 203 | 204 | hellosign.signatureRequest.list(opts).then((res) => { 205 | console.log(res); 206 | }).catch((err) => { 207 | console.error(err); 208 | }); 209 | ```` 210 | 211 | #### Send a Signature Request 212 | 213 | ````javascript 214 | var signers = [ 215 | { 216 | email_address : 'jack@example.com', 217 | name : 'Jack', 218 | order : 0, 219 | sms_phone_number: '+14155550101', 220 | }, 221 | { 222 | email_address : 'jill@example.com', 223 | name : 'Jill', 224 | order : 1, 225 | } 226 | ] 227 | 228 | OR 229 | 230 | // Any of the signers is eligible to sign for the entire group. 231 | var signerGroup = [ 232 | { 233 | group: 'Authorized signatory', 234 | [0]: { 235 | name: 'Jack', 236 | email_address: 'jack@example.com' 237 | }, 238 | [1]: { 239 | name: 'Jill', 240 | email_address: 'jill@example.com' 241 | } 242 | }, 243 | ] 244 | 245 | var options = { 246 | test_mode : 1, 247 | title : 'NDA with Acme Co.', 248 | subject : 'The NDA we talked about', 249 | message : 'Please sign this NDA and then we can discuss more. Let me know if you have any questions.', 250 | signers : signers, 251 | attachments: [ 252 | { 253 | name: 'Example Name', 254 | instructions: 'Example instructions', 255 | signer_index: 1, 256 | required: 1, 257 | }, 258 | ], 259 | cc_email_addresses : ['lawyer@example.com', 'lawyer2@example.com'], 260 | files : ['my/docs/nda.pdf'], 261 | metadata : { 262 | clientId : '1234', 263 | custom_text : 'NDA #9' 264 | } 265 | }; 266 | 267 | hellosign.signatureRequest.send(options) 268 | .then(function(res){ 269 | console.log(res.signature_request); 270 | }); 271 | ```` 272 | 273 | #### Send a Signature Request with Template 274 | 275 | ````javascript 276 | var signers = [ 277 | { 278 | email_address : 'george@example.com', 279 | name : 'George', 280 | role : 'Signer' 281 | } 282 | ] 283 | 284 | OR 285 | 286 | // Any of the signers is eligible to sign for the entire group. 287 | var signerGroup = [ 288 | { 289 | role: 'Signer', 290 | group: 'Authorized signatory', 291 | [0]: { 292 | name: 'George', 293 | email_address: 'george@example.com', 294 | }, 295 | [1]: { 296 | name: 'Gina', 297 | email_address: 'gina@example.com', 298 | } 299 | }, 300 | ] 301 | 302 | var options = { 303 | test_mode : 1, 304 | template_id : '7b63c2131099ef7effeb0e980e2c42005fe3405d', 305 | subject : 'Purchase Order', 306 | message : 'Glad we could come to an agreement.', 307 | signers : signers, 308 | custom_fields: [ 309 | { 310 | name: "start_date", 311 | value: "01/10/2016", 312 | editor: "Signer", 313 | required: true 314 | } 315 | ] 316 | }; 317 | 318 | hellosign.signatureRequest.sendWithTemplate(options) 319 | .then(function(res){ 320 | console.log(res.signature_request); 321 | }); 322 | ```` 323 | 324 | #### Send a reminder about an outstanding signature request 325 | ````javascript 326 | var request_id = 'fa5c8a0b0f492d768749333ad6fcc214c111e967' 327 | var email = 'thedude@abides.com' 328 | hellosign.signatureRequest.remind(request_id,{email_address : email}) 329 | .then(function(res){ 330 | console.log(res.signature_request); 331 | }); 332 | ```` 333 | 334 | #### Update an email address on a signature request 335 | ````javascript 336 | const opts = { 337 | signature_id: '35e3787bd2e61e496099', 338 | email_address: 'mary@example.com', 339 | } 340 | 341 | hellosign.signatureRequest.update('2f9781e1a8e2045224d808c153c2e1d3df6f8f2f', opts).then((res) => { 342 | console.log('what is the response?', res); 343 | }).catch((err) => { 344 | console.error('what is the error?', err); 345 | }); 346 | 347 | ```` 348 | 349 | #### Download files from a signature request 350 | ````javascript 351 | var request_id = 'fa5c8a0b0f492d768749333ad6fcc214c111e967' 352 | hellosign.signatureRequest.download(request_id, {file_type: 'zip'}, function(err, response) { 353 | var file = fs.createWriteStream("files.zip"); 354 | response.pipe(file); 355 | file.on('finish', function() { 356 | file.close(); 357 | }); 358 | }); 359 | ```` 360 | 361 | #### Cancel an outstanding signature request 362 | ````javascript 363 | var request_id = 'fa5c8a0b0f492d768749333ad6fcc214c111e967'; 364 | hellosign.signatureRequest.cancel(request_id) 365 | .then(function(response){ 366 | console.log(response.statusCode); 367 | console.log(response.statusMessage); 368 | }) 369 | .catch(function(err){ 370 | // Handle errors 371 | }); 372 | ```` 373 | See below for more info on the statusCode / statusMessage properties. 374 | 375 | #### Remove Signature Request Access 376 | ````javascript 377 | var request_id = 'fa5c8a0b0f492d768749333ad6fcc214c111e967'; 378 | hellosign.signatureRequest.removeAccess(request_id) 379 | .then(function(response){ 380 | console.log(response.statusCode); 381 | console.log(response.statusMessage); 382 | }) 383 | .catch(function(err){ 384 | // Handle errors 385 | }); 386 | ```` 387 | See below for more info on the statusCode / statusMessage properties. 388 | 389 | #### Send an embedded signature request 390 | ````javascript 391 | var options = { 392 | test_mode : 1, 393 | clientId : '0836272d66a1b53f9822f3aa07aef704', 394 | title : 'NDA with Acme Co.', 395 | subject : 'The NDA we talked about', 396 | message : 'Please sign this NDA and then we can discuss more. Let me know if you have any questions.', 397 | signers : [ 398 | { 399 | email_address : 'jack@example.com', 400 | name : 'Jack', 401 | order : 0, 402 | sms_phone_number: '+14155550101', 403 | },{ 404 | email_address : 'jill@example.com', 405 | name : 'Jill', 406 | order : 1, 407 | } 408 | ], 409 | attachments: [ 410 | { 411 | name: 'Example Name', 412 | instructions: 'Example instructions', 413 | signer_index: 1, 414 | required: 1, 415 | }, 416 | ], 417 | cc_email_addresses : ['lawyer@example.com', 'lawyer@example2.com'], 418 | files : ['my/files/nda.pdf'] 419 | }; 420 | 421 | hellosign.signatureRequest.createEmbedded(options) 422 | .then(function(res){ 423 | console.log(res.signature_request); 424 | }); 425 | 426 | ```` 427 | 428 | #### Send an embedded signature request with a template 429 | ````javascript 430 | var options = { 431 | test_mode : 1, 432 | clientId : '0836272d66a1b53f9822f3aa07aef704', 433 | template_id : '7b63c2131099ef7effeb0e980e2c42005fe3405d', 434 | subject : 'Purchase Order', 435 | message : 'Glad we could come to an agreement.', 436 | signers : [ 437 | { 438 | email_address : 'george@example.com', 439 | name : 'George', 440 | role : 'Signer' 441 | } 442 | ] 443 | }; 444 | 445 | hellosign.signatureRequest.createEmbeddedWithTemplate(options); 446 | .then(function(res){ 447 | console.log(res.signature_request); 448 | }); 449 | ```` 450 | 451 | #### Release On-Hold Signature Request 452 | ````javascript 453 | var request_id = 'fa5c8a0b0f492d768749333ad6fcc214c111e967'; 454 | hellosign.signatureRequest.releaseHold(request_id) 455 | .then(function(response){ 456 | console.log(response); 457 | }) 458 | .catch(function(err){ 459 | // Handle errors 460 | }); 461 | ```` 462 | 463 | ### Embedded 464 | 465 | #### Get an embedded sign URL 466 | ````javascript 467 | var signature_id = 'fa5c8a0b0f492d768749333ad6fcc214c111e967'; 468 | hellosign.embedded.getSignUrl(signature_id) 469 | .then(); 470 | ```` 471 | 472 | #### Get an embedded template's edit URL 473 | ````javascript 474 | // GET 475 | var template_id = '7b63c2131099ef7effeb0e980e2c42005fe3405d'; 476 | hellosign.embedded.getEditUrl(template_id) 477 | .then(); 478 | 479 | // POST 480 | var template_id = '7b63c2131099ef7effeb0e980e2c42005fe3405d'; 481 | 482 | const opts = { 483 | test_mode: 1, 484 | merge_fields: [{"name":"Last Name","type":"text"}] 485 | }; 486 | hellosign.embedded.postEditUrl(template_id, opts) 487 | .then(); 488 | ```` 489 | ### Reports 490 | 491 | #### Gives you insights into user activity and document status 492 | 493 | #### Get a Report 494 | ````javaScript 495 | const opts = { 496 | test_mode: 1, 497 | start_date: '09/13/2020', 498 | end_date: '09/20/2020', 499 | report_type: ['user_activity', 'document_status'] 500 | }; 501 | 502 | client.reports.get(opts).then((res) => { 503 | console.log('what is the response?', res); 504 | }).catch((err) => { 505 | console.error('what is the error?', err); 506 | }); 507 | ```` 508 | 509 | ### OAuth 510 | 511 | #### Get the user to authorize your app 512 | 513 | You'll need to create an API app, and add OAuth support, and then use the URL provided to authorize users for your application. 514 | When you do so, you'll get a state and code value that can be used as below to get an OAuth access token. 515 | 516 | See our [OAuth 2.0 walkthrough](https://www.hellosign.com/api/oauthWalkthrough) for more details. 517 | 518 | #### Get an app-specific access token 519 | ````javascript 520 | var hellosign = require('./hellosign.js')({key: 'YOUR API KEY HERE', client_id: 'your client id', client_secret: 'your client secret'}); 521 | 522 | hellosign.oauth.getToken({state : '53b02619', code : '1d0219ea3363aa67'}) 523 | .then(); 524 | ```` 525 | 526 | #### Make API calls using your OAuth token: 527 | 528 | Use the access token to instantiate an app-specific HelloSign object: 529 | 530 | ````javascript 531 | var hellosignOauth = require('./hellosign-nodejs-sdk/lib/hellosign.js')({oauthToken: 'YOUR_ACCESS_TOKEN'}); 532 | ```` 533 | 534 | You can then use the HelloSign object you've created with the Oauth key to perform requests with that key: 535 | 536 | ````javascript 537 | var options = { 538 | test_mode : 1, 539 | title : 'NDA with Acme Co.', 540 | subject : 'The NDA we talked about', 541 | message : 'Please sign this NDA and then we can discuss more. Let me know if you have any questions.', 542 | signers : [ 543 | { 544 | email_address : 'jack@example.com', 545 | name : 'Jack', 546 | order : 0 547 | }, 548 | { 549 | email_address : 'jill@example.com', 550 | name : 'Jill', 551 | order : 1 552 | } 553 | ], 554 | cc_email_addresses : ['lawyer@example.com', 'lawyer@example2.com'], 555 | files : ['nda.pdf'] 556 | }; 557 | 558 | hellosignOauth.signatureRequest.send(options) 559 | .then(); 560 | ```` 561 | 562 | #### Refresh your OAuth token 563 | 564 | From the HelloSign instance with your app's client_id and secret set, you can also use the refresh token you got in the first token call above to fetch a new access token for your use: 565 | 566 | ````javascript 567 | hellosign.oauth.refreshToken({refresh_token : 'YOUR_REFRESH_TOKEN'}) 568 | .then(); 569 | ```` 570 | 571 | ### Team 572 | 573 | #### Get your team's information 574 | ````javascript 575 | hellosign.team.get() 576 | .then(function(res){ 577 | console.log(res.team); 578 | }); 579 | ```` 580 | 581 | #### Create a team (if you don't have one) 582 | ````javascript 583 | var team_name = 'Radion 6' 584 | hellosign.team.create({name: team_name}) 585 | .then(function(res){ 586 | console.log(res.team); 587 | }); 588 | ```` 589 | 590 | #### Update team 591 | ````javascript 592 | var newName = 'The Mr. T Team'; 593 | hellosign.team.update({name: newName}) 594 | .then(function(res){ 595 | console.log(res.team); 596 | }); 597 | ```` 598 | 599 | #### Remove team 600 | ````javascript 601 | hellosign.team.destroy().then((res) => { 602 | console.log('what is the statusCode?', res.statusCode); 603 | console.log('what is the statusMessage?', res.statusMessage); 604 | }).catch((err) => { 605 | console.error('what is the error?', err); 606 | }); 607 | ```` 608 | 609 | #### Add a team member 610 | ````javascript 611 | var memberEmail = 'benedict@cumberbatch.org'; 612 | hellosign.team.addMember({email_address: memberEmail}) 613 | .then(function(res){ 614 | console.log(res.team); 615 | }); 616 | ```` 617 | 618 | #### Remove a team member 619 | ````javascript 620 | var memberEmail = 'benedictinemonk@cumberbatch.org'; 621 | hellosign.team.removeMember({email_address: memberEmail}) 622 | .then(function(res){ 623 | console.log(res.team); 624 | }); 625 | ```` 626 | 627 | ### Template 628 | 629 | #### List current templates 630 | ````javascript 631 | hellosign.template.list(); 632 | .then(function(res){ 633 | console.log(res.templates); 634 | } 635 | ```` 636 | 637 | #### Get a template by id 638 | ````javascript 639 | var template_id = '7b63c2131099ef7effeb0e980e2c42005fe3405d'; 640 | hellosign.template.get(template_id) 641 | .then(function(res){ 642 | console.log(res.template); 643 | }); 644 | ```` 645 | 646 | #### Add a team member to a template 647 | ````javascript 648 | var template_id = '7b63c2131099ef7effeb0e980e2c42005fe3405d'; 649 | var memberEmail = 'robin@batman.com' 650 | hellosign.template.addUser( 651 | template_id, 652 | { 653 | email_address: memberEmail 654 | } 655 | ) 656 | .then(function(res){ 657 | console.log(res.template); 658 | }); 659 | ```` 660 | 661 | #### Remove a team member from a template 662 | ````javascript 663 | var template_id = '7b63c2131099ef7effeb0e980e2c42005fe3405d'; 664 | var memberEmail = 'othersidekick@batman.com' 665 | hellosign.template.removeUser( 666 | template_id, 667 | { 668 | email_address: memberEmail 669 | } 670 | ) 671 | .then(function(res){ 672 | console.log(res.template); 673 | }); 674 | ```` 675 | 676 | #### Create an embedded template draft 677 | ````javascript 678 | var options = { 679 | test_mode: 1, 680 | files: ['my/files/nda.pdf'], 681 | title: 'embedded draft test', 682 | subject: 'embedded draft test', 683 | message: 'embedded draft test', 684 | signer_roles: [ 685 | { 686 | name: 'Sherlock', 687 | order: 0 688 | },{ 689 | name: 'Watson', 690 | order: 1 691 | } 692 | ], 693 | cc_roles: ['commissioner@metropolice.gov.uk'] 694 | }; 695 | 696 | var results = hellosign.template.createEmbeddedDraft(options) 697 | .then(function(res){ 698 | console.log(res.template); 699 | }); 700 | ```` 701 | 702 | #### Delete Template 703 | ````javascript 704 | hellosign.template.delete(templateId) 705 | .then(function(res){ 706 | console.log(response.statusCode); 707 | console.log(response.statusMessage); 708 | }) 709 | ```` 710 | ### Unclaimed Draft 711 | 712 | #### Create an unclaimed draft 713 | ````javascript 714 | hellosign.unclaimedDraft.create({ 715 | test_mode : 1, 716 | files : ['my/files/nda.pdf', 'other/files/secret.pdf'] 717 | }) 718 | .then(function(res){ 719 | console.log(res.unclaimed_draft.claim_url); 720 | }); 721 | ```` 722 | 723 | #### Create an embedded unclaimed draft 724 | ````javascript 725 | var options = { 726 | test_mode : 1, 727 | clientId : '0836272d66a1b53f9822f3aa07aef704', 728 | type : 'request_signature', 729 | subject : 'The NDA we talked about', 730 | requester_email_address : 'jack@hellosign.com', 731 | files : ['my/secret/lair/nda.pdf'], 732 | is_for_embedded_signing : 1 733 | }; 734 | 735 | hellosign.unclaimedDraft.createEmbedded(options) 736 | .then(function(res){ 737 | console.log(res.unclaimed_draft.claim_url); 738 | }); 739 | ```` 740 | 741 | #### Create embedded unclaimed draft with template 742 | ````javascript 743 | var options = { 744 | test_mode: 1, 745 | template_id: '7b63c2131099ef7effeb0e980e2c42005fe3405d', 746 | title: 'embedded draft test', 747 | subject: 'embedded draft test', 748 | message: 'embedded draft test', 749 | signing_redirect_url: 'http://bondstreet.co.uk', 750 | requesting_redirect_url: 'http://met.police.uk', 751 | 752 | signers: [ 753 | { 754 | name: 'Sherlock', 755 | role: 'Signer', 756 | email_address: 'sherlock@holmesdetective.co.uk', 757 | pin: 3645 758 | },{ 759 | name: 'Watson', 760 | role: 'Assistant', 761 | email_address: 'watson@holmesdetective.co.uk', 762 | pin: 4657 763 | } 764 | ], 765 | attachments: [ 766 | { 767 | name: 'Example Name', 768 | instructions: 'Example instructions', 769 | signer_index: 1, 770 | required: 1, 771 | }, 772 | ], 773 | requester_email_address: 'mrshudson@landlady.com', 774 | metadata: { 775 | clue1: 'pink suitcase', 776 | clue2: 'rache...' 777 | } 778 | }; 779 | 780 | hellosign.unclaimedDraft.createEmbeddedWithTemplate(options); 781 | .then(function(res){ 782 | console.log(res.unclaimed_draft.claim_url); 783 | }); 784 | ```` 785 | 786 | #### Create embedded unclaimed draft edit and resend 787 | ````javascript 788 | var options = { 789 | test_mode: 1, 790 | clientId: 'b6b8e7deaf8f0b95c029dca049356d4a2cf9710a' 791 | }; 792 | 793 | hellosign.unclaimedDraft.editAndResend('2f9781e1a83jdja934d808c153c2e1d3df6f8f2f', opts) 794 | .then((res) => { 795 | console.log(res); 796 | }) 797 | .catch((err) => { 798 | console.error(err); 799 | }); 800 | ```` 801 | 802 | ### API App 803 | 804 | #### Get API App 805 | ````javaScript 806 | hellosign.apiApp.get(clientId).then((res) => { 807 | // handle response 808 | }).catch((err) => { 809 | // handle error 810 | }); 811 | ```` 812 | 813 | #### List API App 814 | ````javascript 815 | hellosign.apiApp.list().then((res) => { 816 | // handle response 817 | }).catch((err) => { 818 | // handle error 819 | }); 820 | ```` 821 | 822 | #### Create API App 823 | ````javascript 824 | const opts = { 825 | name: 'My Cool App', 826 | domain: 'http://www.example.com', 827 | callback_url: 'http://www.example.com/callback', 828 | white_labeling_options: '{"primary_button_color":"#ff0000","primary_button_text_color":"#000000"}' 829 | }; 830 | 831 | hellosign.apiApp.create(opts).then((res) => { 832 | // handle response 833 | }).catch((err) => { 834 | // handle error 835 | }); 836 | ```` 837 | 838 | #### Update API App 839 | ````javascript 840 | const opts = { 841 | name: 'My Cool App', 842 | domain: 'http://www.example.com', 843 | white_labeling_options: '{"primary_button_color":"#ff0000","primary_button_text_color":"#000000"}' 844 | }; 845 | 846 | hellosign.apiApp.update(clientId, opts).then((res) => { 847 | // handle response 848 | }).catch((err) => { 849 | // handle error 850 | }); 851 | ```` 852 | 853 | #### Delete API App 854 | ````javascript 855 | hellosign.apiApp.delete(clientId).then((res) => { 856 | console.log('what is the statusCode?', res.statusCode); 857 | console.log('what is the statusMessage?', res.statusMessage); 858 | }).catch((err) => { 859 | console.error('what is the error?', err); 860 | }); 861 | ```` 862 | 863 | ## Warnings 864 | 865 | Any warnings returned from the api will be accessible on the response object returned: 866 | 867 | ````javascript 868 | hellosign.account.get() 869 | .then(function(res){ 870 | console.log(res.warnings); 871 | }); 872 | ```` 873 | 874 | ## Status codes 875 | 876 | On any response object, you can inspect the `statusCode` and `statusMessage` properties to get HTTP status information. 877 | 878 | This is especially useful for endpoints that don't return any JSON information, like `cancel`: 879 | 880 | ````javascript 881 | hellosign.signatureRequest.cancel('fa5c8a0b0a492d768749333ad6fcc214c111e967') 882 | .then(function(response){ 883 | console.log(response.statusCode); 884 | console.log(response.statusMessage); 885 | }); 886 | ```` 887 | 888 | ## Tests 889 | 890 | Unit tests can be run simply by executing: 891 | ````sh 892 | npm test 893 | ```` 894 | 895 | Mocked functional tests are coming soon. Look inside the repo for a sneak peak. 896 | 897 | ## Additional notes 898 | 899 | ### Local callbacks 900 | We do not allow app callbacks (event or OAuth) to be set to localhost. However it is still possible to test callbacks against a local server. Tunneling services such as ngrok (http://ngrok.com) can help you set this up. 901 | 902 | ## License 903 | 904 | ``` 905 | The MIT License (MIT) 906 | 907 | Copyright (C) 2020 hellosign.com 908 | 909 | Permission is hereby granted, free of charge, to any person obtaining a copy 910 | of this software and associated documentation files (the "Software"), to deal 911 | in the Software without restriction, including without limitation the rights 912 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 913 | copies of the Software, and to permit persons to whom the Software is 914 | furnished to do so, subject to the following conditions: 915 | 916 | The above copyright notice and this permission notice shall be included in all 917 | copies or substantial portions of the Software. 918 | 919 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 920 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 921 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 922 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 923 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 924 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 925 | SOFTWARE. 926 | ``` 927 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/hellosign.js'); 2 | -------------------------------------------------------------------------------- /lib/Error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (C) 2015 hellosign.com 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | var utils = require('./utils'); 28 | 29 | module.exports = _Error; 30 | 31 | /** 32 | * Generic Error klass to wrap any errors returned by HelloSign-node 33 | */ 34 | function _Error(raw) { 35 | this.populate.apply(this, arguments); 36 | this.stack = (new Error(this.message)).stack; 37 | } 38 | 39 | // Extend Native Error 40 | _Error.prototype = Object.create(Error.prototype); 41 | 42 | _Error.prototype.type = 'GenericError'; 43 | _Error.prototype.populate = function(type, message) { 44 | this.type = type; 45 | this.message = message; 46 | }; 47 | 48 | _Error.extend = utils.protoExtend; 49 | 50 | /** 51 | * Create subclass of internal Error klass 52 | * (Specifically for errors returned from HelloSign's REST API) 53 | */ 54 | var HelloSignError = _Error.HelloSignError = _Error.extend({ 55 | type: 'HelloSignError', 56 | populate: function(raw) { 57 | 58 | // Move from prototype def (so it appears in stringified obj) 59 | this.type = this.type; 60 | 61 | this.stack = (new Error(raw.message)).stack; 62 | this.rawType = raw.type; 63 | this.code = raw.code; 64 | this.param = raw.param; 65 | this.message = raw.message; 66 | this.detail = raw.detail; 67 | this.raw = raw; 68 | 69 | } 70 | }); 71 | 72 | // Alias for connection errors 73 | _Error.HelloSignConnectionError = _Error.HelloSignError; 74 | 75 | /** 76 | * Helper factory which takes raw HelloSign errors and outputs wrapping instances 77 | */ 78 | HelloSignError.generate = function(rawHelloSignError) { 79 | return new _Error(rawHelloSignError.error_name, rawHelloSignError.error_msg); 80 | }; 81 | -------------------------------------------------------------------------------- /lib/HelloSignMethod.basic.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (C) 2015 hellosign.com 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | var hellosignMethod = require('./HelloSignMethod'); 28 | var utils = require('./utils'); 29 | 30 | module.exports = { 31 | 32 | create: hellosignMethod({ 33 | method: 'POST' 34 | }), 35 | 36 | list: hellosignMethod({ 37 | method: 'GET' 38 | }), 39 | 40 | retrieve: hellosignMethod({ 41 | method: 'GET', 42 | path: '/{id}', 43 | urlParams: ['id'] 44 | }), 45 | 46 | update: hellosignMethod({ 47 | method: 'POST', 48 | path: '{id}', 49 | urlParams: ['id'] 50 | }), 51 | 52 | // Avoid 'delete' keyword in JS 53 | del: hellosignMethod({ 54 | method: 'DELETE', 55 | path: '{id}', 56 | urlParams: ['id'] 57 | }), 58 | 59 | setMetadata: function(id, key, value, auth, cb) { 60 | 61 | var self = this; 62 | var data = key; 63 | var isObject = utils.isObject(key); 64 | // We assume null for an empty object 65 | var isNull = data === null || (isObject && !Object.keys(data).length); 66 | 67 | // Allow optional passing of auth & cb: 68 | if ((isNull || isObject) && typeof value == 'string') auth = value; 69 | else if (typeof auth != 'string') { 70 | if (!cb && typeof auth == 'function') { 71 | cb = auth; 72 | } 73 | auth = null; 74 | } 75 | 76 | var urlData = this.createUrlData(); 77 | var deferred = this.createDeferred(cb); 78 | var path = this.createFullPath('/' + id, urlData); 79 | 80 | if (isNull) { 81 | // Reset metadata: 82 | sendMetadata(null, auth); 83 | } else if (!isObject) { 84 | // Set individual metadata property: 85 | var metadata = {}; 86 | metadata[key] = value; 87 | sendMetadata(metadata, auth); 88 | } else { 89 | // Set entire metadata object after resetting it: 90 | this._request('POST', path, { 91 | metadata: null 92 | }, auth, function(err, response) { 93 | if (err) return deferred.reject(err); 94 | sendMetadata(data, auth); 95 | }); 96 | } 97 | 98 | function sendMetadata(metadata, auth) { 99 | self._request('POST', path, { 100 | metadata: metadata 101 | }, auth, function(err, response) { 102 | if (err) deferred.reject(err); 103 | else deferred.resolve(response.metadata); 104 | }); 105 | } 106 | 107 | return deferred.promise; 108 | 109 | }, 110 | 111 | getMetadata: function(id, auth, cb) { 112 | 113 | if (!cb && typeof auth == 'function') { 114 | cb = auth; 115 | auth = null; 116 | } 117 | 118 | var urlData = this.createUrlData(); 119 | var deferred = this.createDeferred(cb); 120 | var path = this.createFullPath('/' + id, urlData); 121 | 122 | this._request('GET', path, {}, auth, function(err, response) { 123 | if (err) deferred.reject(err); 124 | else deferred.resolve( 125 | response.metadata 126 | ); 127 | }); 128 | 129 | return deferred.promise; 130 | 131 | } 132 | 133 | }; 134 | -------------------------------------------------------------------------------- /lib/HelloSignMethod.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (C) 2015 hellosign.com 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | var path = require('path'); 28 | var utils = require('./utils'); 29 | var OPTIONAL_REGEX = /^optional!/; 30 | 31 | /** 32 | * Create an API method from the declared spec. 33 | * 34 | * @param [spec.method='GET'] Request Method (POST, GET, DELETE, PUT) 35 | * @param [spec.path=''] Path to be appended to the API BASE_PATH, joined with 36 | * the instance's path (e.g. "charges" or "customers") 37 | * @param [spec.required=[]] Array of required arguments in the order that they 38 | * must be passed by the consumer of the API. Subsequent optional arguments are 39 | * optionally passed through a hash (Object) as the penultimate argument 40 | * (preceeding the also-optional callback argument 41 | */ 42 | module.exports = function hellosignMethod(spec) { 43 | 44 | var commandPath = typeof spec.path == 'function' ? spec.path 45 | : utils.makeURLInterpolator( spec.path || '' ); 46 | var requestMethod = (spec.method || 'GET').toUpperCase(); 47 | var urlParams = spec.urlParams || []; 48 | 49 | return function() { 50 | 51 | var self = this; 52 | var args = [].slice.call(arguments); 53 | 54 | var callback = typeof args[args.length - 1] == 'function' && args.pop(); 55 | var auth = args.length > urlParams.length && utils.isAuthKey(args[args.length - 1]) ? args.pop() : null; 56 | var data = utils.isObject(args[args.length - 1]) ? args.pop() : {}; 57 | var urlData = this.createUrlData(); 58 | if('appRequest' in spec && spec['appRequest'] == true){ 59 | data['client_id'] = this._hellosign.getApiField('clientId'); 60 | } 61 | if('oauthRequest' in spec && spec['oauthRequest'] == true){ 62 | if(!this._hellosign.getApiField('clientId')){ 63 | throw new Error('Oauth request requires client_id param'); 64 | } 65 | data['client_id'] = this._hellosign.getApiField('clientId'); 66 | // Client Secret is only required for OAuth requests 67 | if (this._hellosign.getApiField('clientSecret')){ 68 | data['client_secret'] = this._hellosign.getApiField('clientSecret'); 69 | } 70 | } 71 | var deferred = this.createDeferred(callback); 72 | 73 | for (var i = 0, l = urlParams.length; i < l; ++i) { 74 | 75 | var arg = args[0]; 76 | var param = urlParams[i]; 77 | 78 | var isOptional = OPTIONAL_REGEX.test(param); 79 | param = param.replace(OPTIONAL_REGEX, ''); 80 | 81 | if (!arg) { 82 | if (isOptional) { 83 | urlData[param] = ''; 84 | continue; 85 | } 86 | throw new Error('HelloSign: I require argument "' + urlParams[i] + '", but I got: ' + arg); 87 | } 88 | urlData[param] = args.shift(); 89 | } 90 | 91 | if (args.length) { 92 | throw new Error( 93 | 'HelloSign: Unknown arguments (' + args + '). Did you mean to pass an options object? ' + 94 | 'See https://github.com/hellosign/hellosign-node/wiki/Passing-Options.' 95 | ); 96 | } 97 | var oauthRequest = 'oauthRequest' in spec && spec['oauthRequest'] === true ? true : false; 98 | if(oauthRequest === true){ 99 | if('refresh_token' in data){ 100 | data['grant_type'] = 'refresh_token'; 101 | } else { 102 | data['grant_type'] = 'authorization_code'; 103 | } 104 | } 105 | var requestPath = this.createFullPath(commandPath, urlData, oauthRequest); 106 | 107 | self._request(requestMethod, requestPath, data, auth, function(err, response) { 108 | if (err) { 109 | deferred.reject(err); 110 | } else { 111 | deferred.resolve( 112 | spec.transformResponseData ? 113 | spec.transformResponseData(response) : 114 | response 115 | ); 116 | } 117 | }); 118 | 119 | return deferred.promise; 120 | 121 | }; 122 | }; 123 | -------------------------------------------------------------------------------- /lib/HelloSignResource.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (C) 2015 hellosign.com 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | var http = require('http'); 28 | var https = require('https'); 29 | var path = require('path'); 30 | var FormData = require('form-data'); 31 | var utils = require('./utils'); 32 | var Error = require('./Error'); 33 | var fs = require('fs'); 34 | 35 | var hasOwn = {}.hasOwnProperty; 36 | 37 | // Provide extension mechanism for HelloSign Resource Sub-Classes 38 | HelloSignResource.extend = utils.protoExtend; 39 | 40 | // Expose method-creator & prepared (basic) methods 41 | HelloSignResource.method = require('./HelloSignMethod'); 42 | HelloSignResource.BASIC_METHODS = require('./HelloSignMethod.basic'); 43 | 44 | /** 45 | * Encapsulates request logic for a HelloSign Resource 46 | */ 47 | function HelloSignResource(hellosign, urlData) { 48 | 49 | this._hellosign = hellosign; 50 | this._urlData = urlData || {}; 51 | 52 | this.basePath = utils.makeURLInterpolator(hellosign.getApiField('basePath')); 53 | this.path = utils.makeURLInterpolator(this.path); 54 | 55 | if (this.includeBasic) { 56 | this.includeBasic.forEach(function(methodName) { 57 | this[methodName] = HelloSignResource.BASIC_METHODS[methodName]; 58 | }, this); 59 | } 60 | 61 | this.initialize.apply(this, arguments); 62 | 63 | } 64 | 65 | HelloSignResource.prototype = { 66 | 67 | path: '', 68 | 69 | initialize: function() {}, 70 | 71 | createFullPath: function(commandPath, urlData, oauthRequest) { 72 | 73 | var pathPrefix = oauthRequest === true ? "/" : this.basePath(urlData); 74 | 75 | return path.join( 76 | pathPrefix, 77 | this.path(urlData), 78 | typeof commandPath == 'function' ? 79 | commandPath(urlData) : commandPath 80 | ).replace(/\\/g, '/'); // ugly workaround for Windows 81 | }, 82 | 83 | createUrlData: function() { 84 | var urlData = {}; 85 | // Merge in baseData 86 | for (var i in this._urlData) { 87 | if (hasOwn.call(this._urlData, i)) { 88 | urlData[i] = this._urlData[i]; 89 | } 90 | } 91 | return urlData; 92 | }, 93 | 94 | createDeferred: function(callback) { 95 | return (() => { 96 | let resolve; 97 | let reject; 98 | 99 | let p = new Promise((res, rej) => { 100 | resolve = res; 101 | reject = rej; 102 | }) 103 | 104 | if (callback) { 105 | // Callback, if provided, is a simply translated to Promise'esque: 106 | // (Ensure callback is called outside of promise stack) 107 | p.then(function(res) { 108 | setTimeout(function(){ 109 | callback(null, res) 110 | }, 0); 111 | }).catch(function(err) { 112 | setTimeout(function(){ 113 | callback(err, null); 114 | }, 0); 115 | }); 116 | } 117 | 118 | return { 119 | promise: p, 120 | reject, 121 | resolve 122 | } 123 | })() 124 | }, 125 | 126 | _timeoutHandler: function(timeout, req, callback) { 127 | var self = this; 128 | return function() { 129 | var timeoutErr = new Error('ETIMEDOUT'); 130 | timeoutErr.code = 'ETIMEDOUT'; 131 | 132 | req._isAborted = true; 133 | req.abort(); 134 | 135 | callback.call( 136 | self, 137 | new Error.HelloSignConnectionError({ 138 | message: 'Request aborted due to timeout being reached (' + timeout + 'ms)', 139 | detail: timeoutErr 140 | }), 141 | null 142 | ); 143 | } 144 | }, 145 | 146 | _responseHandler: function(req, callback) { 147 | 148 | var self = this; 149 | return function(res) { 150 | var resHeaders = res.headers; 151 | var statusCode = res.statusCode; 152 | var statusMessage = res.statusMessage; 153 | 154 | if ('content-type' in resHeaders && resHeaders['content-type'].indexOf("application/json") !== -1){ 155 | 156 | var response = ''; 157 | res.setEncoding('utf8'); 158 | res.on('data', function(chunk) { 159 | response += chunk; 160 | }); 161 | res.on('end', function() { 162 | try { 163 | if(response == "\n" || response == ""){ 164 | response = {}; 165 | } else { 166 | response = JSON.parse(response); 167 | } 168 | 169 | response.resHeaders = resHeaders; 170 | response.statusCode = statusCode; 171 | response.statusMessage = statusMessage; 172 | 173 | if (response.error) { 174 | var err; 175 | var err_data = {}; 176 | if(typeof response.error === 'object'){ 177 | err_data = response.error; 178 | } else if(typeof response.error === 'string'){ 179 | err_data['error_name'] = response.error; 180 | err_data['error_msg'] = response.error_description; 181 | } 182 | 183 | err = Error.HelloSignError.generate(err_data); 184 | return callback.call(self, err, null); 185 | } 186 | } catch (e) { 187 | return callback.call( 188 | self, 189 | new Error.HelloSignAPIError({ 190 | message: 'Invalid JSON received from the HelloSign API', 191 | response: response, 192 | exception: e 193 | }), 194 | null 195 | ); 196 | } 197 | callback.call(self, null, response); 198 | }); 199 | } else if ('content-transfer-encoding' in resHeaders && resHeaders['content-transfer-encoding'] == "binary"){ 200 | callback.call(self, null, res); 201 | // Simplified response for code-only statuses 202 | } else if ('content-length' in resHeaders && resHeaders['content-length'] <= "1") { 203 | response = {}; 204 | response.resHeaders = resHeaders; 205 | response.statusCode = statusCode; 206 | response.statusMessage = statusMessage; 207 | callback.call(self, null, response); 208 | } else { 209 | callback.call(self, null, res); 210 | } 211 | }; 212 | }, 213 | 214 | _errorHandler: function(req, callback) { 215 | var self = this; 216 | return function(error) { 217 | if (req._isAborted) return; // already handled 218 | if (!error || !error.message) { 219 | error = { 220 | message: 'An error occurred with our connection to HelloSign', 221 | detail: error 222 | }; 223 | } 224 | callback.call( 225 | self, 226 | new Error.HelloSignConnectionError(error), 227 | null 228 | ); 229 | } 230 | }, 231 | _prepareOpts : function(data){ 232 | 233 | if('signers' in data && Array.isArray(data.signers)){ 234 | for(var i=0; i 'some/url/123/456' 86 | */ 87 | makeURLInterpolator: (function() { 88 | var rc = { 89 | '\n': '\\n', '\"': '\\\"', 90 | '\u2028': '\\u2028', '\u2029': '\\u2029' 91 | }; 92 | return function makeURLInterpolator(str) { 93 | return new Function( 94 | 'o', 95 | 'return "' + ( 96 | str 97 | .replace(/["\n\r\u2028\u2029]/g, function($0) { 98 | return rc[$0]; 99 | }) 100 | .replace(/\{([\s\S]+?)\}/g, '" + encodeURIComponent(o["$1"]) + "') 101 | ) + '";' 102 | ); 103 | }; 104 | }()), 105 | 106 | /** 107 | * Provide simple "Class" extension mechanism 108 | */ 109 | protoExtend: function(sub) { 110 | var Super = this; 111 | var Constructor = hasOwn.call(sub, 'constructor') ? sub.constructor : function() { 112 | Super.apply(this, arguments); 113 | }; 114 | Constructor.prototype = Object.create(Super.prototype); 115 | for (var i in sub) { 116 | if (hasOwn.call(sub, i)) { 117 | Constructor.prototype[i] = sub[i]; 118 | } 119 | } 120 | for (i in Super) { 121 | if (hasOwn.call(Super, i)) { 122 | Constructor[i] = Super[i]; 123 | } 124 | } 125 | return Constructor; 126 | } 127 | 128 | }; 129 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hellosign-sdk", 3 | "version": "2.1.0", 4 | "description": "HelloSign NodeJS SDK", 5 | "homepage": "https://github.com/HelloFax/hellosign-nodejs-sdk", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/HelloFax/hellosign-nodejs-sdk" 9 | }, 10 | "bugs:": "apisupport@hellosign.com", 11 | "engines": { 12 | "node": ">=8.0.0" 13 | }, 14 | "main": "lib/hellosign.js", 15 | "dependencies": { 16 | "form-data": "^2.3.2" 17 | }, 18 | "scripts": { 19 | "test": "mocha test/unit", 20 | "start": "node server.js" 21 | }, 22 | "devDependencies": { 23 | "expect": "^24.9.0", 24 | "express": "^4.17.1", 25 | "expect.js": "^0.3.1", 26 | "mocha": "^7.0.1", 27 | "sinon": "^1.14.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | 4 | app.get('/', function (req, res) { 5 | res.send('Hello World!'); // This will serve your request to '/'. 6 | }); 7 | 8 | app.listen(8080, function () { 9 | console.log('Example app listening on port 8080!'); 10 | }); 11 | -------------------------------------------------------------------------------- /test/_initial.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2015 hellosign.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | var expect = require('expect'); 26 | var params = require('./testparams'); 27 | var hellosign = require('../lib/hellosign'); 28 | var path = require('path'); 29 | 30 | describe('Confirm Test Environment', function(){ 31 | describe('Confirm Params', function(){ 32 | it('should have API Key', function(){ 33 | expect(params.key).to.be.ok(); 34 | expect(params.key).to.not.equal('API_KEY'); 35 | }); 36 | it('should have ClientID', function(){ 37 | expect(params.client_id).to.be.ok(); 38 | expect(params.client_id).to.not.equal('CLIENT_ID'); 39 | }); 40 | }); 41 | describe('Confirm Paths', function(){ 42 | it('should be run from the sdk root', function(){ 43 | var cwd = path.resolve(process.cwd()); 44 | var split = cwd.split('/'); 45 | var last_part = split[split.length-1]; 46 | 47 | expect(last_part).to.equal('hellosign-nodejs-sdk'); 48 | }) 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/hellosign-sdk-test-server/lib/fixtures/NDA.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellosign/hellosign-nodejs-sdk/65d882ae8622885c6f0079ef176489b7fb2bb02d/test/hellosign-sdk-test-server/lib/fixtures/NDA.pdf -------------------------------------------------------------------------------- /test/hellosign-sdk-test-server/lib/fixtures/files.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellosign/hellosign-nodejs-sdk/65d882ae8622885c6f0079ef176489b7fb2bb02d/test/hellosign-sdk-test-server/lib/fixtures/files.zip -------------------------------------------------------------------------------- /test/hellosign-sdk-test-server/lib/helpers.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | var helpers = module.exports = { 4 | // Taken from _prepareOpts in the HelloSign NodeJS SDK - last update = 06/26/2015 5 | formatOptions : function(data){ 6 | 7 | if('signers' in data && Array.isArray(data.signers)){ 8 | for(var i=0; i> make this take params! 264 | 'urlParams': [], 265 | 'body': null, 266 | 'response': { 267 | "list_info": { 268 | "page": 1, 269 | "num_pages": 1, 270 | "num_results": 2, 271 | "page_size": 20 272 | }, 273 | "signature_requests": [ 274 | { 275 | "signature_request_id": "d10338cad145e1cb68afc828", 276 | "title": "FHA", 277 | "subject": "FHA", 278 | "message": "Let me know if you two have any questions.", 279 | "is_complete": false, 280 | "has_error": false, 281 | "custom_fields": [ 282 | ], 283 | "response_data": [ 284 | ], 285 | "signing_url": "https://www.hellosign.com/editor/sign?guid=d10338cad145e1cb68afc828", 286 | "signing_redirect_url": null, 287 | "details_url": "https://www.hellosign.com/home/manage?guid=d10338cad145e1cb68afc828", 288 | "requester_email_address": "me@hellosign.com", 289 | "signatures": [ 290 | { 291 | "signature_id": "78caf2a1d01cd39cea2bc1cbb340dac3", 292 | "signer_email_address": "george-jetson@example.com", 293 | "signer_name": "George Jetson", 294 | "order": 0, 295 | "status_code": "awaiting_signature", 296 | "signed_at": null, 297 | "last_viewed_at": null, 298 | "last_reminded_at": null, 299 | "has_pin" : false 300 | }, 301 | { 302 | "signature_id": "616629ed37f8588d28600be17ab5d6b7", 303 | "signer_email_address": "jane-jetson@example.com", 304 | "signer_name": "Jane Jetson", 305 | "order": 1, 306 | "status_code": "awaiting_signature", 307 | "signed_at": null, 308 | "last_viewed_at": null, 309 | "last_reminded_at": null, 310 | "has_pin" : false 311 | } 312 | ], 313 | "cc_email_addresses": [ 314 | "stan@example.com" 315 | ] 316 | }, 317 | { 318 | "signature_request_id": "fa5c8a0b0f492d768749333a", 319 | "title": "Purchase Agreement", 320 | "subject": "Purchase Agreement", 321 | "message": "Please sign and return.", 322 | "is_complete": true, 323 | "has_error": false, 324 | "custom_fields": [ 325 | ], 326 | "response_data": [ 327 | { 328 | "api_id": "uniqueIdHere_1", 329 | "name": "Needs Express Shipping", 330 | "signature_id": "5687fb7bd5aaacb1689728762b600c74", 331 | "value": true, 332 | "type": "checkbox" 333 | }, 334 | { 335 | "api_id": "uniqueIdHere_2", 336 | "name": "Shipping Address", 337 | "signature_id": "5687fb7bd5aaacb1689728762b600c74", 338 | "value": "1212 Park Avenuee", 339 | "type": "text" 340 | }, 341 | { 342 | "api_id": "uniqueIdHere_3", 343 | "name": "DateSigned", 344 | "signature_id": "5687fb7bd5aaacb1689728762b600c74", 345 | "value": "09/01/2012", 346 | "type": "date_signed" 347 | } 348 | ], 349 | "signing_url": null, 350 | "signing_redirect_url": null, 351 | "details_url": "https://www.hellosign.com/home/manage?guid=fa5c8a0b0f492d768749333a", 352 | "requester_email_address": "me@hellosign.com", 353 | "signatures": [ 354 | { 355 | "signature_id": "5687fb7bd5aaacb1689728762b600c74", 356 | "signer_email_address": "john@example.com", 357 | "signer_name": "John Doe", 358 | "order": null, 359 | "status_code": "signed", 360 | "signed_at": 1346521550, 361 | "last_viewed_at": 1346521483, 362 | "last_reminded_at": null 363 | } 364 | ], 365 | "cc_email_addresses": [ 366 | ] 367 | } 368 | ] 369 | } 370 | }, 371 | // 7 372 | { 373 | 'test_name': 'Get Signature Request Files: Successful', 374 | 'url': '/signature_request/files/:requestId', 375 | 'method': 'GET', 376 | 'acceptsFiles': false, 377 | 'sendsFiles': true, 378 | 'status': 200, 379 | 'body': null, 380 | 'response': null 381 | }, 382 | ] 383 | }; 384 | -------------------------------------------------------------------------------- /test/hellosign-sdk-test-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hellosign-sdk-test-server", 3 | "version": "1.0.0", 4 | "description": "A test utility server for functional tests for HelloSign's SDKs", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "HelloSign (http://hellosign.com)", 10 | "license": "MIT", 11 | "dependencies": { 12 | "body-parser": "^1.13.1", 13 | "express": "^4.13.0", 14 | "multer": "^0.1.8" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/hellosign-sdk-test-server/server.js: -------------------------------------------------------------------------------- 1 | // System 2 | var express = require('express'); 3 | var bodyParser = require('body-parser'); 4 | var multer = require('multer'); 5 | var morgan = require('morgan'); 6 | 7 | // HelloSign 8 | var v3 = require('./lib/v3.spec'); 9 | var helpers = require('./lib/helpers'); 10 | 11 | var app = express(); 12 | app.use(bodyParser.urlencoded({ extended: false })); 13 | app.use(bodyParser.json()); 14 | app.use(multer({dest: './uploads/'})); 15 | app.use(morgan('default')); 16 | 17 | app.get('/', function(req, res){ 18 | res.status(200).send('Welcome to the test server. Please access specific routes to use.'); 19 | }); 20 | 21 | 22 | console.log('Adding v3 endpoints...'); 23 | 24 | v3.tests.forEach(function(test){ 25 | var baseUrl = '/v3' 26 | if (test.url) { 27 | helpers.addTestRouteToApp(test, app, baseUrl); 28 | } 29 | }); 30 | 31 | // Catch everything else 32 | app.route('*') 33 | .all(helpers.sendRouteNotFoundResponse); 34 | 35 | helpers.clearUploads(); 36 | 37 | var server = app.listen('8080'); 38 | 39 | server.on('connection', function(socket) { 40 | helpers.log("A new connection was made by a client."); 41 | socket.setTimeout(30 * 1000); 42 | // 30 second timeout. Change this as you see fit. 43 | }); 44 | -------------------------------------------------------------------------------- /test/hellosign-sdk-test-server/uploads/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /test/mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "recursive": true, 3 | "reporter": "spec", 4 | "timeout": 20000, 5 | } 6 | -------------------------------------------------------------------------------- /test/mock-functional/NDA.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellosign/hellosign-nodejs-sdk/65d882ae8622885c6f0079ef176489b7fb2bb02d/test/mock-functional/NDA.pdf -------------------------------------------------------------------------------- /test/mock-functional/account.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2015 hellosign.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | var expect = require('expect.js'); 26 | var spec = require('../hellosign-sdk-test-server/lib/v3.spec').tests; 27 | var params = require('../testparams.example.js'); 28 | 29 | var hellosign = require('../../lib/hellosign')({ 30 | key: params.key, 31 | client_id: params.client_id, 32 | client_secret: params.client_secret, 33 | dev: false 34 | }); 35 | 36 | hellosign.setHost('localhost', 8080, 'http'); 37 | 38 | describe('Account', function(){ 39 | 40 | describe('Creating, getting, updating, and verification', function(){ 41 | 42 | var email = "node-sdk-test" + new Date().toISOString() + "@example.com"; 43 | 44 | it('should create an account', function(done){ 45 | 46 | var test = spec[2]; 47 | 48 | var result = hellosign.account.create(test['body']) 49 | .then(function(res){ 50 | expect(res.account).to.be.ok(); 51 | done(); 52 | }) 53 | return result; 54 | }); 55 | 56 | it('should verify an account', function(done){ 57 | 58 | var test = spec[4]; 59 | 60 | var result = hellosign.account.verify(test.body) 61 | .then(function(res){ 62 | expect(res.account).to.be.ok(); 63 | done(); 64 | }); 65 | return result; 66 | }); 67 | 68 | it('should get the current account', function(done){ 69 | 70 | var test = spec[0]; 71 | 72 | var result = hellosign.account.get() 73 | .then(function(res){ 74 | expect(res.account).to.be.ok(); 75 | done(); 76 | }); 77 | return result; 78 | }); 79 | 80 | it('should update an account', function(done){ 81 | 82 | var test = spec[1]; 83 | 84 | var result = hellosign.account.update(test.body) 85 | .then(function(res){ 86 | expect(res.account).to.be.ok(); 87 | done(); 88 | }); 89 | return result; 90 | }); 91 | 92 | }); 93 | 94 | }); 95 | -------------------------------------------------------------------------------- /test/mock-functional/signature_request.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2015 hellosign.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | var expect = require('expect'); 26 | var spec = require('../hellosign-sdk-test-server/lib/v3.spec').tests; 27 | var params = require('../testparams.example.js'); 28 | 29 | var hellosign = require('../../lib/hellosign')({ 30 | key: params.key, 31 | client_id: params.client_id, 32 | client_secret: params.client_secret, 33 | dev: false 34 | }); 35 | 36 | hellosign.setHost('localhost', 8080, 'http'); 37 | 38 | var fs = require('fs'); 39 | 40 | describe('Signature Request', function(){ 41 | 42 | describe('Send a signature request', function(){ 43 | 44 | it('should send a request', function(){ 45 | 46 | var test = spec[3]; 47 | test.body.files = [__dirname + '/NDA.pdf']; 48 | 49 | var result = hellosign.signatureRequest.send(test.body) 50 | .then(function(res){ 51 | expect(res.signature_request).to.be.ok(); 52 | done(); 53 | }); 54 | return result; 55 | }); 56 | 57 | it('should send a request with a template', function(){ 58 | 59 | var test = spec[5]; 60 | 61 | var result = hellosign.signatureRequest.sendWithTemplate(test.body) 62 | .then(function(res){ 63 | expect(res.signature_request).to.be.ok(); 64 | done(); 65 | }); 66 | 67 | return result; 68 | }); 69 | 70 | describe('Listing exisitng requests', function(){ 71 | it('should list current signature requests', function(){ 72 | 73 | var test = spec[6]; 74 | 75 | var result = hellosign.signatureRequest.list() 76 | .then(function(res){ 77 | expect(res.signature_requests).to.be.ok(); 78 | done(); 79 | }); 80 | return result; 81 | }); 82 | }); 83 | 84 | describe('Get an existing request', function(){ 85 | xit('should get a current signature request', function(){ 86 | var result = hellosign.signatureRequest.list() 87 | .then(function(res){ 88 | return hellosign.signatureRequest.get(res.signature_requests[0].signature_request_id); 89 | }) 90 | .then(function(res){ 91 | expect(res.signature_request).to.be.ok(); 92 | done(); 93 | }); 94 | return result; 95 | }); 96 | }); 97 | 98 | 99 | }); 100 | 101 | describe('Actions on signature requests', function(){ 102 | 103 | xit('should send a signature request reminder', function(){ 104 | var result = hellosign.signatureRequest.list() 105 | .then(function(res){ 106 | var req_id = res.signature_requests[0].signature_request_id; 107 | var email = res.signature_requests[0].signatures[0].signer_email_address; 108 | return hellosign.signatureRequest.remind(req_id,{email_address : email}); 109 | }) 110 | .then(function(res){ 111 | expect(res.signature_request).to.be.ok(); 112 | done(); 113 | }); 114 | return result; 115 | 116 | }); 117 | 118 | it('should download from a request', function(done){ 119 | 120 | if(fs.existsSync("files.zip")){ 121 | fs.unlinkSync("files.zip"); 122 | } 123 | 124 | hellosign.signatureRequest.list(function(err, success){ 125 | var req_id = success.signature_requests[0].signature_request_id; 126 | hellosign.signatureRequest.download(req_id, {file_type: 'zip'}, function(err, response){ 127 | if(err){ 128 | return done(err); 129 | } 130 | var file = fs.createWriteStream("files.zip"); 131 | response.pipe(file); 132 | file.on('finish', function() { 133 | file.close(); 134 | // if we can close the filestream, we were successful; call done() 135 | done(); 136 | }); 137 | }); 138 | }); 139 | 140 | 141 | }); 142 | 143 | xit('should cancel an existing request', function(){ 144 | hellosign.signatureRequest.list() 145 | .then(function(res){ 146 | var req_id = res.signature_requests[0].signature_request_id; 147 | // No return - should simply run without error 148 | hellosign.signatureRequest.cancel(req_id); 149 | }); 150 | }); 151 | }); 152 | 153 | describe('Embedded requests', function(){ 154 | xit('should send an embedded request', function(){ 155 | 156 | var options = { 157 | test_mode : 1, 158 | clientId : params.client_id, 159 | title : 'NDA with Acme Co.', 160 | subject : 'The NDA we talked about', 161 | message : 'Please sign this NDA and then we can discuss more. Let me know if you have any questions.', 162 | signers : [ 163 | { 164 | email_address : 'jack@example.com', 165 | name : 'Jack', 166 | order : 0, 167 | },{ 168 | email_address : 'jill@example.com', 169 | name : 'Jill', 170 | order : 1, 171 | } 172 | ], 173 | cc_email_addresses : ['lawyer@example.com', 'lawyer@example2.com'], 174 | files : ['test/functional/docs/nda.pdf'] 175 | } 176 | 177 | var result = hellosign.signatureRequest.createEmbedded(options) 178 | .then(function(res){ 179 | expect(res.signature_request).to.be.ok(); 180 | done(); 181 | }); 182 | return result; 183 | 184 | }); 185 | xit('should send an embedded request with a template', function(){ 186 | var options = { 187 | test_mode : 1, 188 | clientId : params.client_id, 189 | template_id : 'TO_BE_POPULATED_BY_PROMISE_RESOLUTION', 190 | subject : 'Purchase Order', 191 | message : 'Glad we could come to an agreement.', 192 | signers : [ 193 | { 194 | email_address : 'george@example.com', 195 | name : 'George', 196 | role : 'Signer' 197 | } 198 | ] 199 | }; 200 | 201 | var result = hellosign.template.list() 202 | .then(function(res){ 203 | options.template_id = res.templates[0].template_id; 204 | return hellosign.signatureRequest.createEmbeddedWithTemplate(options); 205 | }) 206 | .then(function(res){ 207 | expect(res.signature_request).to.be.ok(); 208 | done(); 209 | }); 210 | return result; 211 | }); 212 | }); 213 | 214 | }); 215 | -------------------------------------------------------------------------------- /test/mock-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NODE=`which node` 4 | 5 | if [ -z "$NODE" ]; then 6 | echo 'This script requires NodeJS to be installed on your system globally to work' 7 | fi 8 | 9 | #TODO may need to capture the cwd and then do an npm install... 10 | 11 | #start node server 12 | node hellosign-sdk-test-server & 13 | NODE_PID=$! 14 | 15 | #run tests 16 | ../node_modules/mocha/bin/mocha ./mock-functional/ 17 | 18 | #kill node server 19 | kill $NODE_PID 20 | -------------------------------------------------------------------------------- /test/test-runner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Starting up..." 4 | 5 | echo "Checking for mocha..." 6 | MOCHA="../node_modules/mocha/" 7 | if [[ -e "$MOCHA" ]]; then 8 | #statements 9 | echo "No mocha! Checking for NPM" 10 | NPM=`which npm` 11 | if [[ -z "$NPM" ]]; then 12 | #statements 13 | echo "Cannot run without NPM. Please install." 14 | exit 15 | fi 16 | 17 | echo "NPM found. Installing dependencies" 18 | npm install --dev 19 | 20 | fi 21 | 22 | 23 | echo "Running unit tests" 24 | 25 | ../node_modules/mocha/bin/mocha ./unit 26 | 27 | echo "Running mock functional tests" 28 | 29 | ./mock-tests.sh 30 | 31 | echo "Done!" 32 | -------------------------------------------------------------------------------- /test/testparams.example.js: -------------------------------------------------------------------------------- 1 | /* NOTE: Setup instructions 2 | * Before running the tests, do the following 3 | * =========================================== 4 | * 1. Create an account, upgrade to Business and add an API plan 5 | * 2. Create an api app, set the client id and secret as environment variables 6 | * 3. Create two templates, both with a single role named 'Signer' */ 7 | 8 | var api_key = 'API_KEY'; 9 | var client_id = 'CLIENT_ID'; 10 | var client_secret = 'SECRET'; 11 | 12 | 13 | module.exports = { 14 | key: api_key, 15 | client_id: client_id, 16 | client_secret: client_secret 17 | }; -------------------------------------------------------------------------------- /test/unit/endpoints.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2020 hellosign.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | var expect = require('expect.js'); 26 | 27 | var hellosign = require('../../lib/hellosign.js')({}); 28 | 29 | describe('Function Endpoints', function(){ 30 | describe('Account', function(){ 31 | it('should have account endpoint functions', function(){ 32 | expect(hellosign.account.get).to.be.a('function'); 33 | expect(hellosign.account.create).to.be.a('function'); 34 | expect(hellosign.account.update).to.be.a('function'); 35 | expect(hellosign.account.verify).to.be.a('function'); 36 | }); 37 | }); 38 | describe('Bulk Send', function(){ 39 | it('should have bulk send endpoint functions', function(){ 40 | expect(hellosign.bulkSendJob.get).to.be.a('function'); 41 | expect(hellosign.bulkSendJob.list).to.be.a('function'); 42 | }); 43 | }); 44 | describe('Embedded', function(){ 45 | it('should have embedded endpoint functions', function(){ 46 | expect(hellosign.embedded.getSignUrl).to.be.a('function'); 47 | expect(hellosign.embedded.getEditUrl).to.be.a('function'); 48 | expect(hellosign.embedded.postEditUrl).to.be.a('function'); 49 | }); 50 | }); 51 | describe('OAuth', function(){ 52 | it('should have oauth endpoint functions', function(){ 53 | expect(hellosign.oauth.getToken).to.be.a('function'); 54 | expect(hellosign.oauth.refreshToken).to.be.a('function'); 55 | }); 56 | }); 57 | describe('Signature Request', function(){ 58 | it('should have signature request endpoint functions', function(){ 59 | expect(hellosign.signatureRequest.get).to.be.a('function'); 60 | expect(hellosign.signatureRequest.list).to.be.a('function'); 61 | expect(hellosign.signatureRequest.send).to.be.a('function'); 62 | expect(hellosign.signatureRequest.sendWithTemplate).to.be.a('function'); 63 | expect(hellosign.signatureRequest.remind).to.be.a('function'); 64 | expect(hellosign.signatureRequest.cancel).to.be.a('function'); 65 | expect(hellosign.signatureRequest.removeAccess).to.be.a('function'); 66 | expect(hellosign.signatureRequest.download).to.be.a('function'); 67 | expect(hellosign.signatureRequest.createEmbedded).to.be.a('function'); 68 | expect(hellosign.signatureRequest.createEmbeddedWithTemplate).to.be.a('function'); 69 | expect(hellosign.signatureRequest.bulkCreateEmbeddedWithTemplate).to.be.a('function'); 70 | expect(hellosign.signatureRequest.releaseHold).to.be.a('function'); 71 | 72 | }); 73 | }); 74 | describe('Team', function(){ 75 | it('should have team endpoint functions', function(){ 76 | expect(hellosign.team.get).to.be.a('function'); 77 | expect(hellosign.team.create).to.be.a('function'); 78 | expect(hellosign.team.update).to.be.a('function'); 79 | expect(hellosign.team.destroy).to.be.a('function'); 80 | expect(hellosign.team.addMember).to.be.a('function'); 81 | expect(hellosign.team.removeMember).to.be.a('function'); 82 | }); 83 | }); 84 | describe('Template', function(){ 85 | it('should have template endpoint functions', function(){ 86 | expect(hellosign.template.get).to.be.a('function'); 87 | expect(hellosign.template.list).to.be.a('function'); 88 | expect(hellosign.template.delete).to.be.a('function'); 89 | expect(hellosign.template.addUser).to.be.a('function'); 90 | expect(hellosign.template.removeUser).to.be.a('function'); 91 | expect(hellosign.template.createEmbeddedDraft).to.be.a('function'); 92 | expect(hellosign.template.updatefiles).to.be.a('function'); 93 | }); 94 | }); 95 | describe('Unclaimed Draft', function(){ 96 | it('should have unclaimed draft endpoint functions', function(){ 97 | expect(hellosign.unclaimedDraft.create).to.be.a('function'); 98 | expect(hellosign.unclaimedDraft.createEmbedded).to.be.a('function'); 99 | expect(hellosign.unclaimedDraft.createEmbeddedWithTemplate).to.be.a('function'); 100 | expect(hellosign.unclaimedDraft.editAndResend).to.be.a('function'); 101 | }); 102 | }); 103 | describe('Reports', function(){ 104 | it('should have reports endpoint functions', function(){ 105 | expect(hellosign.reports.get).to.be.a('function'); 106 | }); 107 | }); 108 | }); 109 | -------------------------------------------------------------------------------- /test/unit/errors_and_utilities.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2015 hellosign.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | var expect = require('expect.js'); 26 | var HelloSignError = require('../../lib/Error.js'); 27 | var utils = require('../../lib/utils.js'); 28 | 29 | describe('Error and utility testing', function(){ 30 | 31 | describe('Error class', function(){ 32 | it('should return an HelloSignError error object', function(){ 33 | var myError = new HelloSignError('HelloSignError'); 34 | expect(myError.populate).to.be.a('function'); 35 | expect(myError.type).to.equal('HelloSignError'); 36 | }); 37 | it('should populate error type and message', function(){ 38 | var myError = new HelloSignError(); 39 | myError.populate('HelloSignError', 'message'); 40 | expect(myError.type).to.equal("HelloSignError"); 41 | expect(myError.message).to.equal("message"); 42 | }); 43 | it('should = error types HelloSignError and HelloSignConnectionError', function(){ 44 | expect(HelloSignError.HelloSignError).to.equal(HelloSignError.HelloSignConnectionError); 45 | }) 46 | }); 47 | 48 | describe('Utility functions', function(){ 49 | it('should recognize auth keys', function(){ 50 | var key = 'fx_abcdefghjiklmnopqrstuvwxyz012345'; 51 | expect(utils.isAuthKey(123455)).to.not.be.ok(); 52 | expect(utils.isAuthKey('randomajklfhualkj')).to.not.be.ok(); 53 | expect(utils.isAuthKey(key)).to.be.ok(); 54 | }); 55 | it('should match objects', function(){ 56 | var myObj = {name: 'herman', job: 'mage'}; 57 | expect(utils.isObject(myObj)).to.be.ok(); 58 | }); 59 | it('should stringify request data', function(){ 60 | var myData = { 61 | some: 'thing', 62 | is: 'awesome' 63 | } 64 | var output = utils.stringifyRequestData(myData); 65 | expect(output).to.be.a('string'); 66 | expect(output).to.equal('some=thing&is=awesome'); 67 | }); 68 | it('should create url interpolators', function(){ 69 | var expectedResult = 'my/url/derp/derp2'; 70 | var interpolator = utils.makeURLInterpolator('my/url/{myParam}/{myOtherParam}'); 71 | var output = interpolator({myParam: 'derp', myOtherParam: 'derp2'}); 72 | expect(output).to.equal(expectedResult); 73 | }); 74 | it('should extend prototypes', function(){ 75 | function myClass1(){ 76 | this.prop1 = 'meow'; 77 | this.prop2 = 'mew'; 78 | } 79 | myClass1.extend = utils.protoExtend; 80 | var myClass2 = myClass1.extend({}); 81 | var myInstance = new myClass2(); 82 | expect(myInstance.hasOwnProperty('prop1')).to.be.ok(); 83 | }); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /test/unit/request_utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (C) 2015 hellosign.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | var expect = require('expect.js'); 26 | var params = { key :'FAKE_API_KEY'}; 27 | var hellosign = require('../../lib/hellosign.js')(params); 28 | var path = require('path'); 29 | 30 | describe('Request Utils', function(){ 31 | 32 | describe('_setDefaultHeaders', function(){ 33 | 34 | var headers; 35 | 36 | beforeEach(function(){ 37 | headers = hellosign.account._setDefaultHeaders(); 38 | }); 39 | 40 | it('should set all header types', function(){ 41 | 42 | var defaultHeaders = [ 'Authorization', 'Accept', 'Content-Type', 'User-Agent' ]; 43 | var headerNames = Object.keys(headers); 44 | 45 | for (var i = 0; i < defaultHeaders.length; i++) { 46 | var contained = false; 47 | for (var j = 0; j < headerNames.length; j++) { 48 | if (headerNames[j] === defaultHeaders[i]) { 49 | contained = true; 50 | } 51 | } 52 | expect(contained).to.be.ok(); 53 | } 54 | }); 55 | 56 | it('should set all headers as strings', function() { 57 | 58 | for (header in headers) { 59 | expect(headers[header]).to.be.a('string'); 60 | } 61 | 62 | }); 63 | }); 64 | 65 | describe('_createAuthHeader', function(){ 66 | 67 | it('should set basic auth header for API key', function(){ 68 | var header = hellosign.account._createAuthHeader(); 69 | var authtype = header.split(' ')[0]; 70 | var headerAPIKey = header.split(' ')[1]; 71 | expect(authtype).to.equal('Basic'); 72 | expect(headerAPIKey).to.equal(Buffer.from(params.key + ':').toString('base64')); 73 | }); 74 | 75 | it('should set oath key auth header for oauth token instantiation', function(){ 76 | var testToken = 'YWY0ZTkxMDRjNmNiMzMzZjEyZmMzZmQyMmQ0MWQ4NzAyZDIxMzU5NTYzNTFhNGIwOTE0Mzc0NTFmNDM3YmE2NjdlYzVlOGYw'; 77 | var hellosignOauth = require('../../lib/hellosign.js')({oauthToken: testToken }); 78 | 79 | var header = hellosignOauth.account._createAuthHeader(); 80 | var authtype = header.split(' ')[0]; 81 | var headerToken = header.split(' ')[1]; 82 | expect(authtype).to.equal('Bearer'); 83 | expect(headerToken).to.equal(testToken); 84 | }); 85 | 86 | it('should set username/password key for basic auth instantiation', function(){ 87 | var testUser = {username: 'hodor', password: 'stark'}; 88 | var hellosignBasic = require('../../lib/hellosign.js')(testUser); 89 | var header = hellosignBasic.account._createAuthHeader(); 90 | var authtype = header.split(' ')[0]; 91 | var headerAuthString = header.split(' ')[1]; 92 | expect(authtype).to.equal('Basic'); 93 | expect(headerAuthString).to.equal(Buffer.from(testUser.username + ':' + testUser.password).toString('base64')); 94 | }); 95 | }); 96 | }); 97 | --------------------------------------------------------------------------------