Skip to content

Commit ed55a52

Browse files
author
Balaji Jayaraman
committed
merged how to delete and restore envelope howto code snippets
1 parent 4270a4f commit ed55a52

File tree

12 files changed

+337
-12
lines changed

12 files changed

+337
-12
lines changed

index.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ const {
2929
eg016, eg017, eg018, eg019, eg020, eg022, eg023,
3030
eg024, eg025, eg026, eg027, eg028, eg029, eg030,
3131
eg031, eg032, eg033, eg034, eg035, eg036, eg037,
32-
eg038, eg039, eg040, eg041, eg042, eg043, eg044
32+
eg038, eg039, eg040, eg041, eg042, eg043, eg044,
33+
eg045
3334
} = require('./lib/eSignature/controllers');
3435

3536
const {
@@ -279,7 +280,11 @@ app.get('/eg001', eg001.getController)
279280
.get('/eg043envelopes', eg043.listEnvelopes)
280281
.post('/eg043', eg043.createController)
281282
.get('/eg044', eg044.getController)
282-
.post('/eg044', eg044.createController);
283+
.post('/eg044', eg044.createController)
284+
.get('/eg045', eg045.getDeleteController)
285+
.post('/eg045', eg045.deleteController)
286+
.get('/eg045restore', eg045.getRestoreController)
287+
.post('/eg045restore', eg045.restoreController);
283288

284289
app.get('/cneg001', eg001connect.getController)
285290
.post('/cneg001', eg001connect.createController);
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/**
2+
* @file
3+
* Example 045: Delete and Undelete an Envelope
4+
* @author DocuSign
5+
*/
6+
7+
const path = require('path');
8+
const { deleteEnvelope, moveEnvelopeToFolder, getFolders } = require('../examples/deleteRestoreEnvelope');
9+
const { getExampleByNumber } = require('../../manifestService');
10+
const dsConfig = require('../../../config/index.js').config;
11+
const { API_TYPES, formatString } = require('../../utils.js');
12+
const { getFolderIdByName } = require('../getData.js');
13+
14+
const eg045DeleteRestoreEnvelope = exports;
15+
const exampleNumber = 45;
16+
const eg = `eg0${exampleNumber}`; // This example reference.
17+
const api = API_TYPES.ESIGNATURE;
18+
const mustAuthenticate = '/ds/mustAuthenticate';
19+
const minimumBufferMin = 3;
20+
const restoreEndpoint = `${eg}restore`;
21+
const deleteFolderId = 'recyclebin';
22+
23+
/**
24+
* Delete the envelope
25+
* @param {object} req Request obj
26+
* @param {object} res Response obj
27+
*/
28+
eg045DeleteRestoreEnvelope.deleteController = async (req, res) => {
29+
// Step 1. Check the token
30+
// At this point we should have a good token. But we
31+
// double-check here to enable a better UX to the user.
32+
const isTokenOK = req.dsAuth.checkToken(minimumBufferMin);
33+
if (!isTokenOK) {
34+
req.flash('info', 'Sorry, you need to re-authenticate.');
35+
// Save the current operation so it will be resumed after authentication
36+
req.dsAuth.setEg(req, eg);
37+
return res.redirect(mustAuthenticate);
38+
}
39+
40+
// Step 2. Call the worker method
41+
const args = {
42+
accessToken: req.user.accessToken,
43+
basePath: req.session.basePath,
44+
accountId: req.session.accountId,
45+
envelopeId: req.body.envelopeId,
46+
deleteFolderId: deleteFolderId,
47+
};
48+
let results = null;
49+
50+
try {
51+
results = await deleteEnvelope(args);
52+
} catch (error) {
53+
const errorBody = error?.body || error?.response?.body;
54+
// we can pull the DocuSign error code and message from the response body
55+
const errorCode = errorBody?.errorCode;
56+
const errorMessage = errorBody?.message;
57+
// In production, may want to provide customized error messages and
58+
// remediation advice to the user.
59+
res.render('pages/error', {err: error, errorCode, errorMessage});
60+
}
61+
if (results) {
62+
req.session.envelopeId = req.body.envelopeId;
63+
64+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
65+
const additionalPageData = example.AdditionalPage.find(p => p.Name === 'envelope_is_deleted');
66+
res.render('pages/example_done', {
67+
title: example.ExampleName,
68+
message: formatString(additionalPageData.ResultsPageText, req.body.envelopeId),
69+
redirectUrl: restoreEndpoint,
70+
});
71+
}
72+
};
73+
74+
/**
75+
* Undelete the envelope
76+
* @param {object} req Request obj
77+
* @param {object} res Response obj
78+
*/
79+
eg045DeleteRestoreEnvelope.restoreController = async (req, res) => {
80+
// Step 1. Check the token
81+
// At this point we should have a good token. But we
82+
// double-check here to enable a better UX to the user.
83+
const isTokenOK = req.dsAuth.checkToken(minimumBufferMin);
84+
if (!isTokenOK) {
85+
req.flash('info', 'Sorry, you need to re-authenticate.');
86+
// Save the current operation so it will be resumed after authentication
87+
req.dsAuth.setEg(req, eg);
88+
return res.redirect(mustAuthenticate);
89+
}
90+
91+
const args = {
92+
accessToken: req.user.accessToken,
93+
basePath: req.session.basePath,
94+
accountId: req.session.accountId,
95+
envelopeId: req.session.envelopeId,
96+
fromFolderId: deleteFolderId,
97+
};
98+
const folderName = req.body.folderName;
99+
let folderId = '';
100+
101+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
102+
103+
// Step 2. Call the worker method
104+
let results = null;
105+
try {
106+
const folders = await getFolders(args);
107+
folderId = getFolderIdByName(folders.folders, folderName);
108+
109+
if (!folderId) {
110+
const additionalPageData = example.AdditionalPage.find(page => page.Name === 'folder_does_not_exist');
111+
return res.render('pages/example_done', {
112+
title: example.ExampleName,
113+
message: formatString(additionalPageData.ResultsPageText, folderName),
114+
redirectUrl: restoreEndpoint,
115+
});
116+
117+
}
118+
119+
results = await moveEnvelopeToFolder({ ...args, folderId });
120+
} catch (error) {
121+
const errorBody = error?.body || error?.response?.body;
122+
// we can pull the DocuSign error code and message from the response body
123+
const errorCode = errorBody?.errorCode;
124+
const errorMessage = errorBody?.message;
125+
// In production, may want to provide customized error messages and
126+
// remediation advice to the user.
127+
res.render('pages/error', {err: error, errorCode, errorMessage});
128+
}
129+
if (results) {
130+
res.render('pages/example_done', {
131+
title: example.ExampleName,
132+
message: formatString(example.ResultsPageText, req.session.envelopeId, folderId, folderName),
133+
});
134+
}
135+
};
136+
137+
/**
138+
* Form page for this application
139+
*/
140+
eg045DeleteRestoreEnvelope.getDeleteController = (req, res) => {
141+
// Check that the authentication token is ok with a long buffer time.
142+
// If needed, now is the best time to ask the user to authenticate
143+
// since they have not yet entered any information into the form.
144+
const isTokenOK = req.dsAuth.checkToken();
145+
if (!isTokenOK) {
146+
// Save the current operation so it will be resumed after authentication
147+
req.dsAuth.setEg(req, eg);
148+
return res.redirect(mustAuthenticate);
149+
}
150+
151+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
152+
const sourceFile = (path.basename(__filename))[5].toLowerCase() + (path.basename(__filename)).substr(6);
153+
res.render('pages/examples/eg045DeleteEnvelope', {
154+
eg: eg, csrfToken: req.csrfToken(),
155+
example: example,
156+
envelopeId: req.session.envelopeId,
157+
submitButtonText: res.locals.manifest.SupportingTexts.HelpingTexts.SubmitButtonDeleteText,
158+
sourceFile: sourceFile,
159+
sourceUrl: dsConfig.githubExampleUrl + 'eSignature/examples/' + sourceFile,
160+
documentation: dsConfig.documentation + eg,
161+
showDoc: dsConfig.documentation
162+
});
163+
};
164+
165+
166+
/**
167+
* Form page for this application
168+
*/
169+
eg045DeleteRestoreEnvelope.getRestoreController = (req, res) => {
170+
// Check that the authentication token is ok with a long buffer time.
171+
// If needed, now is the best time to ask the user to authenticate
172+
// since they have not yet entered any information into the form.
173+
const isTokenOK = req.dsAuth.checkToken();
174+
if (!isTokenOK) {
175+
// Save the current operation so it will be resumed after authentication
176+
req.dsAuth.setEg(req, eg);
177+
return res.redirect(mustAuthenticate);
178+
}
179+
180+
if (!req.session.envelopeId) {
181+
return res.redirect(eg);
182+
}
183+
184+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
185+
const sourceFile = (path.basename(__filename))[5].toLowerCase() + (path.basename(__filename)).substr(6);
186+
res.render('pages/examples/eg045RestoreEnvelope', {
187+
eg: eg, csrfToken: req.csrfToken(),
188+
example: example,
189+
envelopeId: req.session.envelopeId,
190+
submitButtonText: res.locals.manifest.SupportingTexts.HelpingTexts.SubmitButtonRestoreText,
191+
sourceFile: sourceFile,
192+
sourceUrl: dsConfig.githubExampleUrl + 'eSignature/examples/' + sourceFile,
193+
documentation: dsConfig.documentation + eg,
194+
showDoc: dsConfig.documentation
195+
});
196+
};

lib/eSignature/controllers/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ module.exports.eg041 = require('./eg041CFREmbeddedSigning');
4141
module.exports.eg042 = require('./eg042DocumentGeneration');
4242
module.exports.eg043 = require('./eg043SharedAccess');
4343
module.exports.eg044 = require('./eg044FocusedView');
44+
module.exports.eg045 = require('./eg045DeleteRestoreEnvelope');
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* @file
3+
* Example 45: Delete and Restore an Envelope
4+
* @author DocuSign
5+
*/
6+
7+
const docusign = require('docusign-esign');
8+
9+
/**
10+
* Moves the envelope to a specified folder
11+
*/
12+
const deleteEnvelope = async (args) => {
13+
//ds-snippet-start:eSign45Step2
14+
const dsApiClient = new docusign.ApiClient();
15+
dsApiClient.setBasePath(args.basePath);
16+
dsApiClient.addDefaultHeader('Authorization', 'Bearer ' + args.accessToken);
17+
const foldersApi = new docusign.FoldersApi(dsApiClient);
18+
//ds-snippet-end:eSign45Step2
19+
20+
//ds-snippet-start:eSign45Step3
21+
const foldersRequest = docusign.FoldersRequest.constructFromObject({
22+
envelopeIds: [args.envelopeId],
23+
});
24+
//ds-snippet-end:eSign45Step3
25+
26+
//ds-snippet-start:eSign45Step4
27+
return await foldersApi.moveEnvelopes(args.accountId, args.deleteFolderId, { foldersRequest });
28+
//ds-snippet-end:eSign45Step4
29+
};
30+
31+
const moveEnvelopeToFolder = async (args) => {
32+
const dsApiClient = new docusign.ApiClient();
33+
dsApiClient.setBasePath(args.basePath);
34+
dsApiClient.addDefaultHeader('Authorization', 'Bearer ' + args.accessToken);
35+
const foldersApi = new docusign.FoldersApi(dsApiClient);
36+
37+
//ds-snippet-start:eSign45Step6
38+
const foldersRequest = docusign.FoldersRequest.constructFromObject({
39+
envelopeIds: [args.envelopeId],
40+
fromFolderId: args.fromFolderId,
41+
});
42+
43+
return await foldersApi.moveEnvelopes(args.accountId, args.folderId, { foldersRequest });
44+
//ds-snippet-end:eSign45Step6
45+
};
46+
47+
/**
48+
* Retrieves the list of folders
49+
*/
50+
const getFolders = async (args) => {
51+
const dsApiClient = new docusign.ApiClient();
52+
dsApiClient.setBasePath(args.basePath);
53+
dsApiClient.addDefaultHeader('Authorization', 'Bearer ' + args.accessToken);
54+
const foldersApi = new docusign.FoldersApi(dsApiClient);
55+
56+
//ds-snippet-start:eSign45Step5
57+
return await foldersApi.list(args.accountId);
58+
//ds-snippet-end:eSign45Step5
59+
};
60+
61+
module.exports = { deleteEnvelope, moveEnvelopeToFolder, getFolders };

lib/eSignature/getData.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,21 @@ async function getUserInfo(accessToken, basePath){
1212
return userInfo;
1313
}
1414

15-
exports.getUserInfo = getUserInfo;
15+
function getFolderIdByName(folders, folderName) {
16+
for (const folder of folders) {
17+
if (folder.name.toLowerCase() === folderName.toLowerCase()) {
18+
return folder.folderId;
19+
}
20+
21+
if (folder.folders?.length > 0) {
22+
const folderId = getFolderIdByName(folder.folders, folderName);
23+
if (folderId) {
24+
return folderId;
25+
}
26+
}
27+
}
28+
29+
return null;
30+
}
31+
32+
module.exports = { getUserInfo, getFolderIdByName };

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"csurf": "^1.11.0",
3737
"docusign-admin": "^3.0.0",
3838
"docusign-click": "^2.2.0",
39-
"docusign-esign": "^8.0.1",
39+
"docusign-esign": "^8.4.0",
4040
"docusign-monitor": "^2.1.0",
4141
"docusign-rooms": "^2.0.0",
4242
"docusign-webforms": "^2.1.0",

test/testHelpers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const path = require('path');
55
const config = require('./testConfig').getConfiguration();
66
const { REDIRECT_URI, BASE_PATH, OAUTH_BASE_PATH, PRIVATE_KEY_FILENAME, EXPIRES_IN, SCOPES, CLICK_SCOPES, ROOM_SCOPES, ADMIN_SCOPES } = require('./constants');
77

8-
const TEST_TIMEOUT_MS = 30000;
8+
const TEST_TIMEOUT_MS = 60000;
99

1010
const apiClient = new docusign.ApiClient({
1111
basePath: BASE_PATH,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<%- include("../../partials/examplesHead") %>
2+
3+
<%- include("../../partials/exampleInfo") %>
4+
5+
<form class="eg" action="" method="post" data-busy="form">
6+
<% if(example.Forms && example.Forms[0].FormName) { %>
7+
<%- example.Forms[0].FormName %>
8+
<% } %>
9+
10+
<div class="form-group">
11+
<label for="envelopeId"><%= example.Forms[0].Inputs[0].InputName %></label>
12+
<input type="text" class="form-control" id="envelopeId" name="envelopeId"
13+
placeholder="<%= example.Forms[0].Inputs[0].InputPlaceholder %>" required
14+
value="<%= envelopeId %>">
15+
<small id="envelopeIdHelp" class="form-text text-muted"><%= locals.manifest.SupportingTexts.HelpingTexts.DefaultEnvelopeId %></small>
16+
17+
</div>
18+
19+
<input type="hidden" name="_csrf" value="<%- csrfToken %>">
20+
<%- include("../../partials/submitButton") %>
21+
</form>
22+
23+
<%- include("../../partials/examplesFoot") %>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<%- include("../../partials/examplesHead") %>
2+
3+
<h4><%= example.ExampleName %></h4>
4+
<p><%- example.ExampleDescription %></p>
5+
<p><%- formatString(locals.manifest.SupportingTexts.HelpingTexts.EnvelopeWillBeRestored, envelopeId) %></p>
6+
7+
<form class="eg" action="" method="post" data-busy="form">
8+
<% if(example.Forms && example.Forms[0].FormName) { %>
9+
<%- example.Forms[0].FormName %>
10+
<% } %>
11+
12+
<div class="form-group">
13+
<label for="folderName"><%= example.Forms[0].Inputs[1].InputName %></label>
14+
<input type="text" class="form-control" id="folderName" name="folderName"
15+
placeholder="<%= example.Forms[0].Inputs[1].InputPlaceholder %>" required>
16+
</div>
17+
18+
<input type="hidden" name="_csrf" value="<%- csrfToken %>">
19+
<%- include("../../partials/submitButton") %>
20+
</form>
21+
22+
<%- include("../../partials/examplesFoot") %>

0 commit comments

Comments
 (0)