Reference Source Test

lib/document.js

  1. 'use strict';
  2. const https = require('https');
  3. const fs = require('fs');
  4. const {
  5. responseHandler, errorHandler, buildRequestOptions,
  6. } = require('./common');
  7.  
  8. // build file multipart/form-data request
  9. const buildFileRequest = (boundaryKey, mimeType, filename) => {
  10. let req = `------FormBoundary${boundaryKey}\r\n`;
  11. req += `Content-Disposition: form-data; name="file"; filename="${filename}"\r\n`;
  12. req += `Content-Type: ${mimeType}\r\n\r\n`;
  13. return req;
  14. };
  15.  
  16. // get file mime type
  17. const getMimeType = path => {
  18. const extTypes = {
  19. doc: 'application/msword',
  20. docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  21. pdf: 'application/pdf',
  22. odt: 'application/vnd.oasis.opendocument.text',
  23. rtf: 'application/rtf',
  24. xml: 'application/xml',
  25. xls: 'application/vnd.ms-excel',
  26. xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  27. ppt: 'application/vnd.ms-powerpoint',
  28. pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  29. png: 'image/png',
  30. jpg: 'image/jpeg',
  31. jpeg: 'image/jpeg',
  32. gif: 'image/gif',
  33. bmp: 'image/bmp',
  34. };
  35. const pathExtension = path.substr(path.lastIndexOf('.')).replace('.', '');
  36. return extTypes[pathExtension.toLowerCase()] || null;
  37. };
  38.  
  39. // get filename from path
  40. const getFileName = path => {
  41. if (path.lastIndexOf('/') !== -1) {
  42. return path.substr(path.lastIndexOf('/')).replace('/', '');
  43. } else {
  44. return path;
  45. }
  46. };
  47.  
  48. // generate randon string
  49. const randString = x => {
  50. let s = '';
  51. while (s.length < x && x > 0) {
  52. const r = Math.random();
  53. s += (r < 0.1 ? Math.floor(r * 100) : String.fromCharCode(Math.floor(r * 26) + (r > 0.5 ? 97 : 65)));
  54. }
  55. return s;
  56. };
  57.  
  58. /**
  59. * Document methods
  60. */
  61. class Document {
  62.  
  63. /**
  64. * Create document payload
  65. * @typedef {Object} DocumentCreateParams
  66. * @property {string} filepath - path to file to be uploaded
  67. * @property {string} token - your auth token
  68. */
  69.  
  70. /**
  71. * Create document response data
  72. * @typedef {Object} DocumentCreateResponse
  73. * @property {string} id - an id of created document
  74. */
  75.  
  76. /**
  77. * Uploads a file and creates a document.
  78. * This endpoint accepts .pdf, .doc, .docx, .odt, .rtf, .png, .jpg, .jpeg, .gif, .bmp, .xml, .xls, .xlsx, .ppt, .pptx file types
  79. * @param {DocumentCreateParams} data - create document payload
  80. * @param {function(err: ApiErrorResponse, res: DocumentCreateResponse)} [callback] - error first node.js callback
  81. */
  82. static create ({ filepath, token }, callback) {
  83. const boundaryKey = randString(30);
  84. const filename = getFileName(filepath);
  85.  
  86. // check mimetype
  87. const mimeType = getMimeType(filepath);
  88.  
  89. if (!mimeType) {
  90. callback('File type isn\'t supported.');
  91. return;
  92. }
  93.  
  94. const file = buildFileRequest(boundaryKey, mimeType, filename);
  95. const stat = fs.statSync(filepath);
  96. const reader = fs.createReadStream(filepath, { bufferSize: 64 * 1024 });
  97. const closingLine = `\r\n------FormBoundary${boundaryKey}--`;
  98. const req = https
  99. .request(buildRequestOptions({
  100. method: 'POST',
  101. path: '/document',
  102. authorization: {
  103. type: 'Bearer',
  104. token,
  105. },
  106. headers: {
  107. 'Content-Type': `multipart/form-data; boundary=----FormBoundary${boundaryKey}`,
  108. 'Content-Length': stat.size + file.toString().length + closingLine.length,
  109. },
  110. }), responseHandler(callback))
  111. .on('error', errorHandler(callback));
  112.  
  113. req.write(file);
  114. reader.on('end', () => {
  115. reader.unpipe(req);
  116. req.end(closingLine);
  117. });
  118.  
  119. // pipe the file and append a closing line
  120. reader.pipe(req, { end: false });
  121. }
  122.  
  123. /**
  124. * Upload document with field extract payload
  125. * @typedef {Object} DocumentFieldExtractParams
  126. * @property {string} filepath - path to file to be uploaded
  127. * @property {string} token - your auth token
  128. */
  129.  
  130. /**
  131. * Upload document with field extract response data
  132. * @typedef {Object} DocumentFieldExtractResponse
  133. * @property {string} id - an id of created document
  134. */
  135.  
  136. /**
  137. * Uploads a file that contains SignNow Document Field Tags.
  138. * This endpoint only accepts .pdf (you may convert the document from .doc or .docx to .pdf)
  139. * @param {DocumentFieldExtractParams} data - upload document with field extract payload
  140. * @param {function(err: ApiErrorResponse, res: DocumentFieldExtractResponse)} [callback] - error first node.js callback
  141. */
  142. static fieldextract ({ filepath, token }, callback) {
  143. const boundaryKey = randString(30);
  144. const filename = getFileName(filepath);
  145.  
  146. // check mimetype
  147. const mimeType = getMimeType(filepath);
  148.  
  149. if (!mimeType) {
  150. callback('File type isn\'t supported.');
  151. return;
  152. }
  153.  
  154. const file = buildFileRequest(boundaryKey, mimeType, filename);
  155. const stat = fs.statSync(filepath);
  156. const reader = fs.createReadStream(filepath, { bufferSize: 64 * 1024 });
  157. const closingLine = `\r\n------FormBoundary${boundaryKey}--`;
  158.  
  159. const req = https
  160. .request(buildRequestOptions({
  161. method: 'POST',
  162. path: '/document/fieldextract',
  163. authorization: {
  164. type: 'Bearer',
  165. token,
  166. },
  167. headers: {
  168. 'Content-Type': `multipart/form-data; boundary=----FormBoundary${boundaryKey}`,
  169. 'Content-Length': stat.size + file.toString().length + closingLine.length,
  170. },
  171. }), responseHandler(callback))
  172. .on('error', errorHandler(callback));
  173.  
  174. req.write(file);
  175. reader.on('end', () => {
  176. reader.unpipe(req);
  177. req.end(closingLine);
  178. });
  179.  
  180. // pipe the file and append a closing line
  181. reader.pipe(req, { end: false });
  182. }
  183.  
  184.  
  185. /**
  186. * Payload to receive user's document list
  187. * @typedef {Object} DocumentListParams
  188. * @property {string} token - your auth token
  189. */
  190.  
  191. /**
  192. * Document list response item
  193. * @typedef {Object} DocumentListItem
  194. * @property {string} id - id of specific document
  195. * @property {string} user_id - id of user that uploaded document
  196. * @property {string} document_name - name of document
  197. * @property {string} page_count - amount of pages in the document
  198. * @property {string} created - timestamp document was created
  199. * @property {string} updated - timestamp document was updated
  200. * @property {string} original_filename - original filename with document format (.pdf, .doc, etc...)
  201. * @property {?string} origin_document_id - an id of original document (if document is a copy)
  202. * @property {string} owner - email of document owner
  203. * @property {boolean} template - is document a template or not
  204. * @property {?string} origin_user_id - id of user who created document
  205. * @property {Object} thumbnail - thumbnail urls with different sizes (small, medium, large)
  206. * @property {Object[]} signatures - signature signed elements
  207. * @property {Object[]} texts - text and enumeration filled elements
  208. * @property {Object[]} checks - checkbox checked elements
  209. * @property {Object[]} tags - field types document contains
  210. * @property {Object[]} fields - all document fillable fields
  211. * @property {Object[]} requests - requests for signing document
  212. * @property {Object[]} roles - document signer roles
  213. * @property {Object[]} field_invites - sent invites data
  214. * @property {number} version_time - document updated timestamp
  215. * @property {Object[]} enumeration_options - dropdown options
  216. * @property {Object[]} attachments - attachments in document
  217. * @property {Object[]} routing_details - signing steps routing details
  218. * @property {Object[]} integrations - document integrations
  219. * @property {Object[]} hyperlinks - document hyperlinks
  220. * @property {Object[]} radiobuttons - checked radiobuttons after document was signed
  221. * @property {Object[]} document_group_template_info - document group templates data
  222. * @property {Object[]} document_group_info - document group template's data
  223. * @property {Object[]} originator_organization_settings - specific organization settings for document
  224. * @property {Object[]} field_validators - validation rules for fields (date fields, payment fields etc.)
  225. * @property {Object} settings - specific document settings
  226. */
  227.  
  228. /**
  229. * User's Document list response
  230. * @typedef {DocumentListItem[]} DocumentListResponse
  231. */
  232.  
  233. /**
  234. * Retrieve user's document list
  235. * @param {DocumentListParams} data - payload to receive user's document list
  236. * @param {function(err: ApiErrorResponse, res: DocumentListItem)} [callback] - error first node.js callback
  237. */
  238. static list ({ token }, callback) {
  239. https
  240. .request(buildRequestOptions({
  241. method: 'GET',
  242. path: '/user/documentsv2',
  243. authorization: {
  244. type: 'Bearer',
  245. token,
  246. },
  247. }), responseHandler(callback))
  248. .on('error', errorHandler(callback))
  249. .end();
  250. }
  251.  
  252. /**
  253. * Document view payload
  254. * @typedef {Object} DocumentViewParams
  255. * @property {string} id - id of specific document
  256. * @property {string} token - your auth token
  257. */
  258.  
  259. /**
  260. * Document view response of specific document
  261. * @typedef {Object} DocumentViewResponse
  262. * @property {string} id - id of specific document
  263. * @property {string} user_id - id of user that uploaded document
  264. * @property {string} document_name - name of document
  265. * @property {string} page_count - amount of pages in the document
  266. * @property {string} created - timestamp document was created
  267. * @property {string} updated - timestamp document was updated
  268. * @property {string} original_filename - original filename with document format (.pdf, .doc, etc...)
  269. * @property {?string} origin_document_id - an id of original document (if document is a copy)
  270. * @property {string} owner - email of document owner
  271. * @property {boolean} template - is document a template or not
  272. * @property {Object} thumbnail - thumbnail urls with different sizes (small, medium, large)
  273. * @property {Object[]} signatures - signature signed elements
  274. * @property {Object[]} texts - text and enumeration filled elements
  275. * @property {Object[]} checks - checkbox checked elements
  276. * @property {Object[]} tags - field types document contains
  277. * @property {Object[]} fields - all document fillable fields
  278. * @property {Object[]} requests - requests for signing document
  279. * @property {Object[]} roles - document signer roles
  280. * @property {Object[]} field_invites - sent invites data
  281. * @property {number} version_time - document updated timestamp
  282. * @property {Object[]} enumeration_options - dropdown options
  283. * @property {Object[]} attachments - attachments in document
  284. * @property {Object[]} routing_details - signing steps routing details
  285. * @property {Object[]} integrations - document integrations
  286. * @property {Object[]} hyperlinks - document hyperlinks
  287. * @property {Object[]} radiobuttons - checked radiobuttons after document was signed
  288. * @property {Object[]} document_group_template_info - document group template's data
  289. * @property {Object[]} document_group_info - document group's data
  290. * @property {Object[]} originator_organization_settings - specific organization settings for document
  291. * @property {Object[]} field_validators - validation rules for fields (date fields, payment fields etc.)
  292. * @property {Object} settings - specific document settings
  293. * @property {string} parent_id - id of document folder
  294. * @property {string} originator_logo - logo url
  295. * @property {Object[]} pages - list of document pages with page sources and page size.
  296. */
  297.  
  298. /**
  299. * Retrieves a document detailed data
  300. * @param {DocumentViewParams} data - view document details payload
  301. * @param {function(err: ApiErrorResponse, res: DocumentViewResponse)} [callback] - error first node.js callback
  302. */
  303. static view ({ id, token }, callback) {
  304. https
  305. .request(buildRequestOptions({
  306. method: 'GET',
  307. path: `/document/${id}`,
  308. authorization: {
  309. type: 'Bearer',
  310. token,
  311. },
  312. }), responseHandler(callback))
  313. .on('error', errorHandler(callback))
  314. .end();
  315. }
  316.  
  317. /**
  318. * @typedef {Object} SignatureField
  319. * @property {number} page_number - page number of document
  320. * @property {string} type - type of field (signature)
  321. * @property {string} name - unique name of the field
  322. * @property {string} role - role name
  323. * @property {boolean} required - required field
  324. * @property {number} height - height of the field
  325. * @property {number} width - width of the field
  326. * @property {number} x - X coordinate of the field
  327. * @property {number} y - Y coordinate of the field
  328. * @property {string} [label] - field label
  329. */
  330.  
  331. /**
  332. * @typedef {Object} InitialsField
  333. * @property {number} page_number - page number of document
  334. * @property {string} type - type of field (initials)
  335. * @property {string} name - unique name of the field
  336. * @property {string} role - role name
  337. * @property {boolean} required - required field
  338. * @property {number} height - height of the field
  339. * @property {number} width - width of the field
  340. * @property {number} x - X coordinate of the field
  341. * @property {number} y - Y coordinate of the field
  342. * @property {string} [label] - field label
  343. */
  344.  
  345. /**
  346. * @typedef {Object} TextField
  347. * @property {number} page_number - page number of document
  348. * @property {string} type - type of field (text)
  349. * @property {string} name - unique name of the field
  350. * @property {string} role - role name
  351. * @property {boolean} required - required field
  352. * @property {number} height - height of the field
  353. * @property {number} width - width of the field
  354. * @property {number} x - X coordinate of the field
  355. * @property {number} y - Y coordinate of the field
  356. * @property {string|number} [prefilled_text] - prefilled field value
  357. * @property {string} [validator_id] - field value validator ID
  358. * @property {string} [label] - field label
  359. */
  360.  
  361. /**
  362. * @typedef {Object} CheckField
  363. * @property {number} page_number - page number of document
  364. * @property {string} type - type of field (checkbox)
  365. * @property {string} name - unique name of the field
  366. * @property {string} role - role name
  367. * @property {boolean} required - required field
  368. * @property {number} height - height of the field
  369. * @property {number} width - width of the field
  370. * @property {number} x - X coordinate of the field
  371. * @property {number} y - Y coordinate of the field
  372. * @property {boolean} [prefilled_text] - prefilled field value
  373. * @property {string} [label] - field label
  374. */
  375.  
  376. /**
  377. * @typedef {Object} EnumerationField
  378. * @property {number} page_number - page number of document
  379. * @property {string} type - type of field (enumeration)
  380. * @property {string} name - unique name of the field
  381. * @property {string} role - role name
  382. * @property {boolean} required - required field
  383. * @property {number} height - height of the field
  384. * @property {number} width - width of the field
  385. * @property {number} x - X coordinate of the field
  386. * @property {number} y - Y coordinate of the field
  387. * @property {Array<number|string>} enumeration_options - choice options of enumerable field
  388. * @property {string|number} [prefilled_text] - prefilled field value
  389. * @property {string} [label] - field label
  390. * @property {boolean} [custom_defined_option=false] - if 'true' user can change values, if 'false' user can only read
  391. */
  392.  
  393. /**
  394. * @typedef {Object} RadioButton
  395. * @property {number} page_number - page number of document
  396. * @property {number} height - height of the field
  397. * @property {number} width - width of the field
  398. * @property {number} x - X coordinate of the field
  399. * @property {number} y - Y coordinate of the field
  400. * @property {string} value - value of radio button
  401. * @property {string} checked - radio button check status
  402. * @property {number} created - redio button creation timestamp
  403. */
  404.  
  405. /**
  406. * @typedef {Object} RadioButtonField
  407. * @property {number} page_number - page number of document
  408. * @property {string} type - type of field (radiobutton)
  409. * @property {string} name - unique name of the field
  410. * @property {string} role - role name
  411. * @property {boolean} required - required field
  412. * @property {number} height - height of the field
  413. * @property {number} width - width of the field
  414. * @property {number} x - X coordinate of the field
  415. * @property {number} y - Y coordinate of the field
  416. * @property {RadioButton[]} radio - array of radio buttons
  417. * @property {string|number} [prefilled_text] - prefilled field value
  418. * @property {string} [label] - field label
  419. */
  420.  
  421. /**
  422. * @typedef {Object} FormulaTreeBranchTerminator
  423. * @property {string} term - name of field which will participate in calculation formula
  424. */
  425.  
  426. /**
  427. * @typedef {Object} FormulaTreeBranch
  428. * @property {FormulaTree} term - a part of formula parsed into tree
  429. */
  430.  
  431. /**
  432. * @typedef {Object} FormulaTree
  433. * @property {string} operator - arithmetic operator's token
  434. * @property {FormulaTreeBranch|FormulaTreeBranchTerminator} left - left branch of formula tree
  435. * @property {FormulaTreeBranch|FormulaTreeBranchTerminator} right - right branch of formula tree
  436. */
  437.  
  438. /**
  439. * @typedef {Object} CalculatedField
  440. * @property {number} page_number - page number of document
  441. * @property {string} type - type of field (text)
  442. * @property {string} name - unique name of the field
  443. * @property {string} role - role name
  444. * @property {boolean} required - required field
  445. * @property {number} height - height of the field
  446. * @property {number} width - width of the field
  447. * @property {number} x - X coordinate of the field
  448. * @property {number} y - Y coordinate of the field
  449. * @property {string} formula - arithmetic formula, e.g. 'TextName+EnumerationName'
  450. * @property {FormulaTree} calculation_formula - formula parsed into tree
  451. * @property {number} calculation_precision - calculation precision
  452. * @property {string} [label] - field label
  453. * @property {boolean} [custom_defined_option=false] - if 'true' user can change values, if 'false' user can only read
  454. */
  455.  
  456. /**
  457. * @typedef {Object} AttachmentField
  458. * @property {number} page_number - page number of document
  459. * @property {string} type - type of field (attachment)
  460. * @property {string} name - unique name of the field
  461. * @property {string} role - role name
  462. * @property {boolean} required - required field
  463. * @property {number} height - height of the field
  464. * @property {number} width - width of the field
  465. * @property {number} x - X coordinate of the field
  466. * @property {number} y - Y coordinate of the field
  467. * @property {string} [label] - field label
  468. */
  469.  
  470. /**
  471. * @typedef {Object} DocumentFields
  472. * @property {number} client_timestamp - local time of user
  473. * @property {Array<SignatureField|InitialsField|TextField|CheckField|EnumerationField|RadioButtonField|CalculatedField|AttachmentField>} fields - all types of fields
  474. */
  475.  
  476. /**
  477. * Update document payload
  478. * @typedef {Object} DocumentUpdateParams
  479. * @property {string} id - id of specific document
  480. * @property {DocumentFields} fields - set of fields
  481. * @property {string} token - your auth token
  482. */
  483.  
  484. /**
  485. * Update document response data
  486. * @typedef {Object} DocumentUpdateResponse
  487. * @property {string} id - an id of document
  488. * @property {Object[]} signatures - signature and initial elements
  489. * @property {Object[]} texts - text and enumeration elements
  490. * @property {Object[]} checks - checkbox elements
  491. * @property {Object[]} attachments - attachment elements
  492. * @property {Object[]} radiobuttons - radiobutton elements
  493. */
  494.  
  495. /**
  496. * Updates an existing document.
  497. * Adds fields (signature, text, initials, checkbox, radiobutton group, calculated) and elements (signature, text, check).
  498. * Every call of this method will add only specified fields into document.
  499. * Old fields will be removed
  500. * @param {DocumentUpdateParams} data - update document request payload
  501. * @param {function(err: ApiErrorResponse, res: DocumentUpdateResponse)} [callback] - error first node.js callback
  502. */
  503. static update ({
  504. id,
  505. fields,
  506. token,
  507. }, callback) {
  508. const JSONData = JSON.stringify(fields);
  509. const req = https
  510. .request(buildRequestOptions({
  511. method: 'PUT',
  512. path: `/document/${id}`,
  513. authorization: {
  514. type: 'Bearer',
  515. token,
  516. },
  517. headers: {
  518. 'Content-Type': 'application/json',
  519. 'Content-Length': Buffer.byteLength(JSONData),
  520. },
  521. }), responseHandler(callback))
  522. .on('error', errorHandler(callback));
  523.  
  524. req.write(JSONData);
  525. req.end();
  526. }
  527.  
  528. /**
  529. * Document download payload
  530. * @typedef {Object} DocumentDownloadParams
  531. * @property {string} id - id of specific document
  532. * @property {string} token - your auth token
  533. */
  534.  
  535. /**
  536. * Document binary data
  537. * @typedef {Buffer} DocumentDownloadResponse
  538. */
  539.  
  540. /**
  541. * Downloads document with embedded fields and elements
  542. * @param {DocumentDownloadParams} data - download document request payload
  543. * @param {function(err: ApiErrorResponse, res: DocumentDownloadResponse)} [callback] - error first node.js callback
  544. */
  545.  
  546. static download ({ id, token }, callback) {
  547. https
  548. .request(buildRequestOptions({
  549. method: 'GET',
  550. path: `/document/${id}/download?type=collapsed`,
  551. authorization: {
  552. type: 'Bearer',
  553. token,
  554. },
  555. }), responseHandler(callback, { parse: false }))
  556. .on('error', errorHandler(callback))
  557. .end();
  558. }
  559.  
  560. // creates a one-time use URL for anyone to download the document as a PDF.
  561. static share ({ id, token }, callback) {
  562. https
  563. .request(buildRequestOptions({
  564. method: 'POST',
  565. path: `/document/${id}/download/link`,
  566. authorization: {
  567. type: 'Bearer',
  568. token,
  569. },
  570. }), responseHandler(callback))
  571. .on('error', errorHandler(callback))
  572. .end();
  573. }
  574.  
  575. /**
  576. * Document invite signer settings
  577. * @typedef {Object} SignerSettings
  578. * @property {string} email - signer's email
  579. * @property {string} role - role name
  580. * @property {string} role_id - role unique id (can be discovered in document details)
  581. * @property {number} order - role signing order
  582. * @property {string} reassign - allow reassign signer
  583. * @property {string} decline_by_signature - signer can decline invite
  584. * @property {number} reminder - remind via email in days
  585. * @property {number} expiration_days - expiration of invite in days
  586. * @property {string} [subject] - specify subject of email
  587. * @property {string} [message] - specify body of email
  588. */
  589.  
  590. /**
  591. * Document invite settings
  592. * @typedef {Object} DocumentInviteSettings
  593. * @property {string} from - email of sender
  594. * @property {SignerSettings[]|string} to - array of signers or email of single signer
  595. * @property {string} [document_id] - an id of document
  596. * @property {string[]} [cc] - array with emails of copy receivers
  597. * @property {string} [subject] - specify subject of email
  598. * @property {string} [message] - specify body of email
  599. * @property {string} [on_complete] - on signing complete action
  600. */
  601.  
  602. /**
  603. * Document invite optional settings
  604. * @typedef {Object} DocumentInviteOptions
  605. * @property {string} [email] - ability to disable invitation by email. to disabale email assign value 'disable'
  606. */
  607.  
  608. /**
  609. * Create document invite payload
  610. * @typedef {Object} DocumentInviteParams
  611. * @property {string} id - an id of document
  612. * @property {DocumentInviteSettings} data - document invite settings
  613. * @property {DocumentInviteOptions} [options] - document invite optional settings
  614. * @property {string} token - your auth token
  615. */
  616.  
  617. /**
  618. * Create document field invite response data
  619. * @typedef {Object} DocumentFieldInviteResponse
  620. * @property {string} status - status of invitation, e.g. 'success'
  621. */
  622.  
  623. /**
  624. * Create document free form invite response data
  625. * @typedef {Object} DocumentFreeformInviteResponse
  626. * @property {string} result - free form invitation result, e.g. 'success'
  627. * @property {string} id - unique id of invite
  628. * @property {string} callback_url - url for front-end redirect or 'none'
  629. */
  630.  
  631. /**
  632. * Create document invite response data
  633. * @typedef {DocumentFieldInviteResponse|DocumentFreeformInviteResponse} DocumentInviteResponse
  634. */
  635.  
  636. /**
  637. * Creates an invite to sign a document.
  638. * You can create a simple free form invite(s) or a role-based invite(s).
  639. * @param {DocumentInviteParams} data - create document invite payload
  640. * @param {function(err: ApiErrorResponse, res: DocumentInviteResponse)} [callback] - error first node.js callback
  641. */
  642. static invite ({
  643. id,
  644. data,
  645. options: { email } = {},
  646. token,
  647. }, callback) {
  648. const JSONData = JSON.stringify(data);
  649. let path = `/document/${id}/invite`;
  650.  
  651. if (email === 'disable') {
  652. path = `${path}?email=disable`;
  653. }
  654.  
  655. const req = https
  656. .request(buildRequestOptions({
  657. method: 'POST',
  658. authorization: {
  659. type: 'Bearer',
  660. token,
  661. },
  662. headers: {
  663. 'Content-Type': 'application/json',
  664. 'Content-Length': Buffer.byteLength(JSONData),
  665. },
  666. path,
  667. }), responseHandler(callback))
  668. .on('error', errorHandler(callback));
  669.  
  670. req.write(JSONData);
  671. req.end();
  672. }
  673.  
  674. // cancel an invite to a document.
  675. static cancelInvite ({ id, token }, callback) {
  676. https
  677. .request(buildRequestOptions({
  678. method: 'PUT',
  679. path: `/document/${id}/fieldinvitecancel`,
  680. authorization: {
  681. type: 'Bearer',
  682. token,
  683. },
  684. }), responseHandler(callback))
  685. .on('error', errorHandler(callback))
  686. .end();
  687. }
  688.  
  689. /**
  690. * Merge documents optional settings
  691. * @typedef {Object} DocumentMergeOptions
  692. * @property {boolean} [removeOriginalDocuments=false] - if true original documents will be removed
  693. */
  694.  
  695. /**
  696. * Merge documents payload
  697. * @typedef {Object} DocumentMergeParams
  698. * @property {string} token - your auth token
  699. * @property {string} name - new name for merged document
  700. * @property {string[]} document_ids - an array of document IDs
  701. * @property {DocumentMergeOptions} options - merge documents optional settings
  702. */
  703.  
  704. /**
  705. * Merge documents response data
  706. * @typedef {Object} DocumentMergeResponse
  707. * @property {string} document_id - an ID of merged document
  708. */
  709.  
  710. /**
  711. * Merges existing documents into one.
  712. * By default original documents are not removed after merging. To remove original documents set `removeOriginalDocuments` option to `true`.
  713. * @param {DocumentMergeParams} data - merge documents payload
  714. * @param {function(err: ApiErrorResponse, res: DocumentMergeResponse)} [originalCallback] - error first node.js callback
  715. */
  716. static merge ({
  717. name,
  718. document_ids,
  719. options: { removeOriginalDocuments = false } = {},
  720. token,
  721. }, originalCallback) {
  722. const JSONData = JSON.stringify({
  723. upload_document: true,
  724. document_ids,
  725. name,
  726. });
  727.  
  728. const callbackWithDocumentDeletion = (mergeErr, mergeRes) => {
  729. if (mergeErr) {
  730. originalCallback(mergeErr);
  731. return;
  732. } else {
  733.  
  734. const removeDocument = ({ id }) => new Promise((resolve, reject) => {
  735. Document.remove({
  736. id,
  737. token,
  738. }, (err, res) => {
  739. if (err) {
  740. reject(err);
  741. } else {
  742. resolve(res.status === 'success');
  743. }
  744. });
  745. });
  746.  
  747. const documentsDeleted = document_ids
  748. .map(id => removeDocument({ id }));
  749.  
  750. Promise.all(documentsDeleted)
  751. .then(deletionStatuses => deletionStatuses.reduce((generalStatus, currentStatus) => (generalStatus && currentStatus), true))
  752. .then(allDocumentsDeleted => {
  753. if (allDocumentsDeleted) {
  754. originalCallback(null, mergeRes);
  755. return;
  756. } else {
  757. throw new Error('Some of the original documents were not removed');
  758. }
  759. })
  760. .catch(removeErr => {
  761. originalCallback(removeErr);
  762. return;
  763. });
  764.  
  765. }
  766. };
  767.  
  768. const callback = removeOriginalDocuments
  769. ? callbackWithDocumentDeletion
  770. : originalCallback;
  771.  
  772. const req = https
  773. .request(buildRequestOptions({
  774. method: 'POST',
  775. path: '/document/merge',
  776. authorization: {
  777. type: 'Bearer',
  778. token,
  779. },
  780. headers: {
  781. 'Content-Type': 'application/json',
  782. 'Content-Length': Buffer.byteLength(JSONData),
  783. },
  784. }), responseHandler(callback))
  785. .on('error', errorHandler(originalCallback));
  786.  
  787. req.write(JSONData);
  788. req.end();
  789. }
  790.  
  791.  
  792.  
  793.  
  794.  
  795.  
  796.  
  797.  
  798. /**
  799. * Document history payload
  800. * @typedef {Object} DocumentHistoryParams
  801. * @property {string} id - id of specific document
  802. * @property {string} token - your auth token
  803. */
  804.  
  805. /**
  806. * Document action log item
  807. * @typedef {Object} DocumentEvent
  808. * @property {string} client_app_name - name of a client application
  809. * @property {?string} client_timestamp - client application timestamp
  810. * @property {string} created - timestamp of action creation
  811. * @property {string} email - email of an actor
  812. * @property {string} event - name of event
  813. * @property {string} ip_address - actor's IP address
  814. * @property {?string} element_id - an ID of specific element
  815. * @property {?string} field_id - an ID of specific field
  816. * @property {?string} json_attributes - field attributes (e.g. font, style, size etc.)
  817. * @property {string} [document_id] - an ID of specific document
  818. * @property {string} [origin] - an origin of document
  819. * @property {string} [unique_id] - an ID of event
  820. * @property {string} [user_agent] - user agent
  821. * @property {string} [user_id] - id of an actor
  822. * @property {string} [version] - version of signed document (incremented after each signature addition)
  823. */
  824.  
  825. /**
  826. * Document history response data
  827. * @typedef {DocumentEvent[]} DocumentHistoryResponse
  828. */
  829.  
  830. /**
  831. * Retrieves a document action log (history) data
  832. * @param {DocumentHistoryParams} data - payload
  833. * @param {function(err: ApiErrorResponse, res: DocumentHistoryResponse)} [callback] - error first node.js callback
  834. */
  835. static history ({ id, token }, callback) {
  836. https
  837. .request(buildRequestOptions({
  838. method: 'GET',
  839. path: `/document/${id}/history`,
  840. authorization: {
  841. type: 'Bearer',
  842. token,
  843. },
  844. }), responseHandler(callback))
  845. .on('error', errorHandler(callback))
  846. .end();
  847. }
  848.  
  849. /**
  850. * Remove document payload
  851. * @typedef {Object} DocumentRemoveParams
  852. * @property {string} id - id of specific document
  853. * @property {string} token - your auth token
  854. */
  855.  
  856. /**
  857. * Remove document response data
  858. * @typedef {Object} DocumentRemoveResponse
  859. * @property {string} status - status of deletion, e.g. 'success'
  860. */
  861.  
  862. /**
  863. * Removes a specified document from folder
  864. * @param {DocumentRemoveParams} data - remove document payload
  865. * @param {function(err: ApiErrorResponse, res: DocumentRemoveResponse)} [callback] - error first node.js callback
  866. */
  867. static remove ({ id, token }, callback) {
  868. https
  869. .request(buildRequestOptions({
  870. method: 'DELETE',
  871. path: `/document/${id}`,
  872. authorization: {
  873. type: 'Bearer',
  874. token,
  875. },
  876. }), responseHandler(callback))
  877. .on('error', errorHandler(callback))
  878. .end();
  879. }
  880.  
  881. }
  882.  
  883. module.exports = Document;