lib/document-group/index.js
'use strict';
const https = require('https');
const {
responseHandler,
errorHandler,
buildRequestOptions,
} = require('../common');
const { view } = require('../document');
const { duplicate } = require('../template');
/**
* @type {function}
* @param {DocumentViewParams} data - view document with field extract payload
* @return {Promise<DocumentViewResponse, ApiErrorResponse>}
*/
const getDocumentDetails = ({ id, token }) => new Promise((resolve, reject) => {
view({
id,
token,
}, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
/**
* @type {function}
* @param {DocumentViewResponse} document
* @return {boolean} is there any field invite in given document
*/
const hasFieldInvites = document => document.field_invites.length > 0;
/**
* @type {function}
* @param {DocumentViewResponse} document
* @return {boolean} is document a template or not
*/
const isTemplate = document => document.template === true;
/**
* @type {function}
* @param {TemplateDuplicateParams} data - duplicate template payload
* @return {Promise<string, ApiErrorResponse>} resolves with id of new document
*/
const makeDocumentFromTemplate = ({ id, token }) => new Promise((resolve, reject) => {
duplicate({
id,
token,
}, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res.id);
}
});
});
/**
* Document Group methods
*/
class DocumentGroup {
/**
* Create document group payload
* @typedef {Object} DocumentGroupCreateParams
* @property {string} token - your auth token
* @property {string[]} ids - array of document or template ids for document group creation
* @property {string} group_name - new name of document group
*/
/**
* Create document group response data
* @typedef {Object} DocumentGroupCreateResponse
* @property {string} id - document group unique id
*/
/**
* Creates document group with specified documents or templates.
* At least one document or template must contain fields. Documents should not contain pending invites.
* @param {DocumentGroupCreateParams} data - create document group payload
* @param {function(err: ApiErrorResponse, res: DocumentGroupCreateResponse)} [callback] - error first node.js callback
*/
static create ({
token,
ids,
group_name,
}, callback) {
const realDocumentIDs = ids
.map(id => {
return getDocumentDetails({
id,
token,
})
.then(document => {
if (isTemplate(document)) {
return makeDocumentFromTemplate({
id,
token,
});
} else if (hasFieldInvites(document)) {
throw new Error('Document in the group must have no pending invites');
} else {
return id;
}
});
});
Promise.all(realDocumentIDs)
.then(document_ids => {
const JSONData = JSON.stringify({
document_ids,
group_name,
});
const req = https
.request(buildRequestOptions({
method: 'POST',
path: '/documentgroup',
authorization: {
type: 'Bearer',
token,
},
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(JSONData),
},
}), responseHandler(callback))
.on('error', errorHandler(callback));
req.write(JSONData);
req.end();
})
.catch(err => {
callback(err.message);
return;
});
}
/**
* Document Group view payload
* @typedef {Object} DocumentGroupViewParams
* @property {string} id - id of specific Document Group
* @property {string} token - your auth token
*/
/**
* Details of single Document in group
* @typedef {Object} DocumentGroupItem
* @property {string} id - an ID of Document
* @property {string} document_name - a name of Document
* @property {string} origin_document_id - an ID of Document origin
* @property {boolean} has_credit_card_number - Document has Credit Card number attached
* @property {boolean} has_unassigned_field - Document has unassigned field(s)
* @property {DocumentThumbnails} thumbnail - thumbnail urls with different sizes (small, medium, large)
* @property {string[]} roles - list of template signer roles
*/
/**
* Document Group view response data
* @typedef {Object} DocumentGroupViewResponse
* @property {string} id - an ID of Document Group
* @property {string} group_name - a name of Document Group
* @property {DocumentGroupItem[]} documents - each single document details
* @property {?string} invite_id - an ID of active invite
* @property {Object[]} originator_organization_settings - originator organization settings
*/
/**
* Retrieves a Document Group detailed data
* @param {DocumentGroupViewParams} data - view Document Group details payload
* @param {function(err: ApiErrorResponse, res: DocumentGroupViewResponse)} [callback] - error first node.js callback
*/
static view ({ id, token }, callback) {
https
.request(buildRequestOptions({
method: 'GET',
path: `/documentgroup/${id}`,
authorization: {
type: 'Bearer',
token,
},
}), responseHandler(callback))
.on('error', errorHandler(callback))
.end();
}
/**
* Document Group invite email configurations
* @typedef {Object} DocumentGroupInviteEmail
* @property {string} email - signer's email
* @property {string} [subject] - subject of invitation email
* @property {string} [message] - content of invitation email
* @property {number} [expiration_days] - expiration of invite in days
* @property {number} [reminder] - number of days in which to remind about invitation via email
*/
/**
* Document Group signer's authentication with password config
* @typedef {Object} DocumentGroupInviteAuthenticationPassword
* @property {string} type - set `password` to authenticate signer with password
* @property {string} value - authentication password value
*/
/**
* Document Group signer's authentication with phone call config
* @typedef {Object} DocumentGroupInviteAuthenticationPhoneCall
* @property {string} type - set `phone` to authenticate signer with phone call
* @property {string} method - set `phone_call` to authenticate signer with phone call
* @property {string} phone - phone number
*/
/**
* Document Group signer's authentication with phone SMS config
* @typedef {Object} DocumentGroupInviteAuthenticationPhoneSMS
* @property {string} type - set `phone` to authenticate signer with phone SMS
* @property {string} method - set `sms` to authenticate signer with phone SMS
* @property {string} phone - phone number
*/
/**
* Document Group invite action configurations
* @typedef {Object} DocumentGroupInviteAction
* @property {string} email - signer's email
* @property {string} role_name - signer's role name
* @property {string} action - name of action with document in signing step
* @property {string} document_id - ID of document in specific signing step
* @property {string} [allow_reassign] - allow reassigning of signer
* @property {string} [decline_by_signature] - signer can decline invite
* @property {DocumentGroupInviteAuthenticationPassword|DocumentGroupInviteAuthenticationPhoneCall|DocumentGroupInviteAuthenticationPhoneSMS} [authentication] - signer's authentication configuration
*/
/**
* Document Group invite step configurations
* @typedef {Object} DocumentGroupInviteStep
* @property {number} order - an order number of invitation step
* @property {DocumentGroupInviteEmail[]} [invite_emails] - Document Group invite emails settings
* @property {DocumentGroupInviteAction[]} invite_actions - Document Group invite actions settings
*/
/**
* Document Group invite completion email configuration
* @typedef {Object} DocumentGroupInviteCompletionEmailConfig
* @property {string} email - email for completion (only from the signers and document owner emails)
* @property {number} disable_document_attachment - disable attachment of signed document group to completion email (values: 0|1)
*/
/**
* Document Group invite settings
* @typedef {Object} DocumentGroupInviteSettings
* @property {DocumentGroupInviteStep[]} invite_steps - Document Group invite steps settings
* @property {DocumentGroupInviteCompletionEmailConfig[]} [completion_emails] - set of completion email configutrations
*/
/**
* Create Document Group invite payload
* @typedef {Object} DocumentGroupInviteParams
* @property {string} id - ID of specific Document Group
* @property {DocumentGroupInviteSettings} data - Document Group invite settings data
* @property {string} token - your auth token
*/
/**
* Create Document Group invite response data
* @typedef {Object} DocumentGroupInviteResponse
* @property {string} id - ID of created Document Group invitation
* @property {?string} pending_invite_link - pending invite link
*/
/**
* Creates an invite to sign a Document Group
* @param {DocumentGroupInviteParams} data - create Document Group invite payload
* @param {function(err: ApiErrorResponse, res: DocumentGroupInviteResponse)} [callback] - error first node.js callback
*/
static invite ({
id,
data,
token,
}, callback) {
const {
invite_steps,
completion_emails,
} = data;
const JSONData = JSON.stringify({
invite_steps,
completion_emails,
});
const req = https
.request(buildRequestOptions({
method: 'POST',
path: `/documentgroup/${id}/groupinvite`,
authorization: {
type: 'Bearer',
token,
},
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(JSONData),
},
}), responseHandler(callback))
.on('error', errorHandler(callback));
req.write(JSONData);
req.end();
}
/**
* Document Group download payload
* @typedef {Object} DocumentGroupDownloadParams
* @property {string} id - an ID of a Document Group
* @property {string} token - your auth token
* @property {string} [type='zip'] - type of download flow, can be either `zip` or `merged`
* @property {string} [with_history='no'] - type of history merging flow, can be `no`, `after_each_document`, or `after_merged_pdf`
* @property {string[]} [document_order] - document order for merging flow
*/
/**
* Document Group binary data
* @typedef {Buffer} DocumentGroupDownloadResponse
*/
/**
* Downloads a document group.
* By default document group will be downloaded as a zip archive and without history.
* To download document group as a single merged PDF set `type` to `merged`.
* To download document group with history set `with_history` to `after_each_document` or `after_merged_pdf`.
* @param {DocumentGroupDownloadParams} data - download Document Group request payload
* @param {function(err: ApiErrorResponse, res: DocumentGroupDownloadResponse)} [callback] - error first node.js callback
*/
static download ({
id,
token,
type = 'zip',
with_history = 'no',
document_order = [],
}, callback) {
const JSONData = JSON.stringify({
type,
with_history,
document_order,
});
const req = https
.request(buildRequestOptions({
method: 'POST',
path: `/documentgroup/${id}/downloadall`,
authorization: {
type: 'Bearer',
token,
},
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(JSONData),
},
}), responseHandler(callback))
.on('error', errorHandler(callback));
req.write(JSONData);
req.end();
}
/**
* Cancel Document Group Invite payload
* @typedef {Object} DocumentGroupCancelInviteParams
* @property {string} id - ID of specific Document Group
* @property {string} invite_id - ID of Document Group Invite
* @property {string} token - your auth token
*/
/**
* Cancel Document Group Invite response data
* @typedef {Object} DocumentGroupCancelInviteResponse
* @property {string} status - status of invite cancellation (e.g. "success")
*/
/**
* Cancels an Invite to sign a specific Document Group
* @param {DocumentGroupCancelInviteParams} data - cancel Document Group Invite payload
* @param {function(err: ApiErrorResponse, res: DocumentGroupCancelInviteResponse)} [callback] - error first node.js callback
*/
static cancelInvite ({
token,
id,
inviteId,
}, callback) {
https
.request(buildRequestOptions({
method: 'POST',
path: `/documentgroup/${id}/groupinvite/${inviteId}/cancelinvite`,
authorization: {
type: 'Bearer',
token,
},
}), responseHandler(callback))
.on('error', errorHandler(callback))
.end();
}
}
module.exports = DocumentGroup;