Skip to content

Commit c9f0dc8

Browse files
authored
Merge pull request #160 from docusign/feature/webforms-example
Web Forms code example
2 parents a8d7ade + 35a671e commit c9f0dc8

File tree

18 files changed

+535
-5
lines changed

18 files changed

+535
-5
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,7 @@ secret
6868
.vs
6969

7070
# ignore RSA keys
71-
private.key
71+
private.key
72+
73+
# ignore Web Form config
74+
web-form-config.json

config/appsettings.example.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,6 @@
2929
"clickAPIUrl": "https://demo.docusign.net/clickapi",
3030
"adminAPIUrl": "https://api-d.docusign.net/management",
3131
"monitorApiUrl": "https://lens-d.docusign.net",
32+
"webformsApiUrl": "https://demo.services.docusign.net/webforms/v1.1",
3233
"codeExamplesManifest": "https://raw.githubusercontent.com/docusign/code-examples-csharp/master/manifest/CodeExamplesManifest.json"
3334
}
44.7 KB
Binary file not shown.

demo_documents/web-form-config.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const {
5454
} = require('./lib/admin/controllers');
5555

5656
const { eg001connect } = require('./lib/connect/controllers');
57+
const { eg001webforms } = require('./lib/webforms/controllers');
5758

5859
const PORT = process.env.PORT || 3000;
5960
const HOST = process.env.HOST || 'localhost';
@@ -272,6 +273,12 @@ app.get('/eg001', eg001.getController)
272273
app.get('/cneg001', eg001connect.getController)
273274
.post('/cneg001', eg001connect.createController);
274275

276+
app.get('/weg001', eg001webforms.getController)
277+
.get('/weg001webForm', eg001webforms.getWebFormCreateController)
278+
.post('/weg001', eg001webforms.createWebFormTemplate)
279+
.post('/weg001webForm', eg001webforms.createWebFormInstance);
280+
281+
275282
function dsLoginCB1(req, res, next) { req.dsAuthCodeGrant.oauth_callback1(req, res, next); }
276283
function dsLoginCB2(req, res, next) { req.dsAuthCodeGrant.oauth_callback2(req, res, next); }
277284

@@ -316,8 +323,11 @@ const ADMIN_SCOPES = [
316323
'user_data_redact', 'asset_group_account_read', 'asset_group_account_clone_write',
317324
'asset_group_account_clone_read'
318325
];
326+
const WEBFORMS_SCOPES = [
327+
'webforms_read', 'webforms_instance_read', 'webforms_instance_write'
328+
];
319329

320-
const scope = [...ROOM_SCOPES, ...CLICK_SCOPES, ...MONITOR_SCOPES, ...ADMIN_SCOPES, ...SCOPES];
330+
const scope = [...ROOM_SCOPES, ...CLICK_SCOPES, ...MONITOR_SCOPES, ...ADMIN_SCOPES, ...WEBFORMS_SCOPES, ...SCOPES];
321331

322332
// Configure passport for DocusignStrategy
323333
let docusignStrategy = new DocusignStrategy({

lib/DSJwtAuth.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ let DsJwtAuth = function _DsJwtAuth(req) {
1818
this.accountName = req.user && req.user.accountName;
1919
this.basePath = req.user && req.user.basePath;
2020
this._tokenExpiration = req.user && req.user.tokenExpirationTimestamp;
21-
this.scopes = 'signature dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms click.manage click.send organization_read group_read permission_read user_read user_write account_read domain_read identity_provider_read user_data_redact asset_group_account_read asset_group_account_clone_write asset_group_account_clone_read';
21+
this.scopes = 'signature dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms click.manage click.send organization_read group_read permission_read user_read user_write account_read domain_read identity_provider_read user_data_redact asset_group_account_read asset_group_account_clone_write asset_group_account_clone_read webforms_read webforms_instance_read webforms_instance_write';
2222

2323
// For production use, you'd want to store the refresh token in non-volatile storage since it is
2424
// good for 30 days. You'd probably want to encrypt it too.

lib/common/DSJwtAuth.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
this.accountName = req.user && req.user.accountName;
1919
this.basePath = req.user && req.user.basePath;
2020
this._tokenExpiration = req.user && req.user.tokenExpirationTimestamp;
21-
this.scopes = 'signature dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms click.manage click.send';
21+
this.scopes = 'signature dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms click.manage click.send webforms_read webforms_instance_read webforms_instance_write';
2222

2323
// For production use, you'd want to store the refresh token in non-volatile storage since it is
2424
// good for 30 days. You'd probably want to encrypt it too.

lib/utils.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const fs = require('fs-extra');
12
const docusign = require('docusign-esign');
23

34
function formatString(inputString) {
@@ -17,6 +18,7 @@ const API_TYPES = {
1718
ROOMS: 'Rooms',
1819
ADMIN: 'Admin',
1920
CONNECT: 'Connect',
21+
WEBFORMS: 'WebForms',
2022
};
2123

2224
async function isCFR(accessToken, accountId, basePath) {
@@ -29,4 +31,10 @@ async function isCFR(accessToken, accountId, basePath) {
2931
return accountDetailsData['status21CFRPart11'];
3032
}
3133

32-
module.exports = { formatString, isCFR, API_TYPES };
34+
function replaceTemplateId(filePath, templateId) {
35+
let content = fs.readFileSync(filePath, 'utf8');
36+
content = content.replace(/template-id/g, templateId);
37+
fs.writeFileSync(filePath, content, 'utf8');
38+
}
39+
40+
module.exports = { formatString, isCFR, replaceTemplateId, API_TYPES };
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/**
2+
* @file
3+
* Example 001: Create and embed an instance of a Web Form
4+
* @author DocuSign
5+
*/
6+
7+
const path = require('path');
8+
const { API_TYPES, replaceTemplateId } = require('../../utils.js');
9+
const { getExampleByNumber } = require('../../manifestService');
10+
const dsConfig = require('../../../config/index.js').config;
11+
const { createWebFormInstance, createWebFormTemplate, listWebForms } = require('../examples/createInstance');
12+
13+
const eg001CreateInstance = exports;
14+
const exampleNumber = 1;
15+
const eg = `weg00${exampleNumber}`; // This example reference.
16+
const api = API_TYPES.WEBFORMS;
17+
const mustAuthenticate = '/ds/mustAuthenticate';
18+
const minimumBufferMin = 3;
19+
const demoDocsPath = path.resolve(__dirname, '../../../demo_documents');
20+
const docFile = 'World_Wide_Corp_Web_Form.pdf';
21+
const webFormConfigFile = 'web-form-config.json';
22+
23+
/**
24+
* Create the web form template
25+
* @param {object} req Request obj
26+
* @param {object} res Response obj
27+
*/
28+
eg001CreateInstance.createWebFormTemplate = 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+
templateName: 'Web Form Example Template',
46+
docFile: path.resolve(demoDocsPath, docFile),
47+
};
48+
let webFormTemplateId = null;
49+
50+
try {
51+
webFormTemplateId = await createWebFormTemplate(args);
52+
replaceTemplateId(path.resolve(demoDocsPath, webFormConfigFile), webFormTemplateId);
53+
} catch (error) {
54+
const errorBody = error && error.response && error.response.body;
55+
// we can pull the DocuSign error code and message from the response body
56+
const errorCode = errorBody && errorBody.errorCode;
57+
const errorMessage = errorBody && errorBody.message;
58+
// In production, may want to provide customized error messages and
59+
// remediation advice to the user.
60+
return res.render('pages/error', { err: error, errorCode, errorMessage });
61+
}
62+
if (webFormTemplateId) {
63+
req.session.webFormTemplateId = webFormTemplateId;
64+
return res.redirect(`/${eg}webForm`);
65+
}
66+
};
67+
68+
/**
69+
* Create the web form instance
70+
* @param {object} req Request obj
71+
* @param {object} res Response obj
72+
*/
73+
eg001CreateInstance.createWebFormInstance = async (req, res) => {
74+
// Step 1. Check the token
75+
// At this point we should have a good token. But we
76+
// double-check here to enable a better UX to the user.
77+
const isTokenOK = req.dsAuth.checkToken(minimumBufferMin);
78+
if (!isTokenOK) {
79+
req.flash('info', 'Sorry, you need to re-authenticate.');
80+
// Save the current operation so it will be resumed after authentication
81+
req.dsAuth.setEg(req, eg);
82+
return res.redirect(mustAuthenticate);
83+
}
84+
85+
// Step 2. Call the worker method
86+
const args = {
87+
accessToken: req.user.accessToken,
88+
basePath: dsConfig.webformsApiUrl,
89+
accountId: req.session.accountId,
90+
clientUserId: '1234-5678-abcd-ijkl',
91+
formName: 'Web Form Example Template',
92+
};
93+
let results = null;
94+
95+
try {
96+
const forms = await listWebForms(args);
97+
results = await createWebFormInstance(forms.items[0].id, args);
98+
} catch (error) {
99+
const errorBody = error && error.response && error.response.body;
100+
// we can pull the DocuSign error code and message from the response body
101+
const errorCode = errorBody && errorBody.errorCode;
102+
const errorMessage = errorBody && errorBody.message;
103+
// In production, may want to provide customized error messages and
104+
// remediation advice to the user.
105+
return res.render('pages/error', { err: error, errorCode, errorMessage });
106+
}
107+
if (results) {
108+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
109+
110+
return res.render('pages/webforms-examples/eg001WebFormEmbed', {
111+
example: example,
112+
instanceToken: results.instanceToken,
113+
integrationKey: dsConfig.dsClientId,
114+
formUrl: results.formUrl,
115+
});
116+
}
117+
};
118+
119+
/**
120+
* Form page for this application
121+
*/
122+
eg001CreateInstance.getController = (req, res) => {
123+
// Check that the authentication token is ok with a long buffer time.
124+
// If needed, now is the best time to ask the user to authenticate
125+
// since they have not yet entered any information into the form.
126+
const isTokenOK = req.dsAuth.checkToken();
127+
if (!isTokenOK) {
128+
// Save the current operation so it will be resumed after authentication
129+
req.dsAuth.setEg(req, eg);
130+
return res.redirect(mustAuthenticate);
131+
}
132+
133+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
134+
const sourceFile =
135+
path.basename(__filename)[5].toLowerCase() +
136+
path.basename(__filename).substr(6);
137+
return res.render('pages/webforms-examples/eg001CreateInstance', {
138+
eg: eg,
139+
csrfToken: req.csrfToken(),
140+
example: example,
141+
sourceFile: sourceFile,
142+
sourceUrl: dsConfig.githubExampleUrl + 'webforms/examples/' + sourceFile,
143+
documentation: dsConfig.documentation + eg,
144+
showDoc: dsConfig.documentation,
145+
});
146+
};
147+
148+
/**
149+
* Form page for this application
150+
*/
151+
eg001CreateInstance.getWebFormCreateController = (req, res) => {
152+
// Check that the authentication token is ok with a long buffer time.
153+
// If needed, now is the best time to ask the user to authenticate
154+
// since they have not yet entered any information into the form.
155+
const isTokenOK = req.dsAuth.checkToken();
156+
if (!isTokenOK) {
157+
// Save the current operation so it will be resumed after authentication
158+
req.dsAuth.setEg(req, eg);
159+
return res.redirect(mustAuthenticate);
160+
}
161+
162+
if (!req.session.webFormTemplateId) {
163+
return res.redirect(`/${eg}`);
164+
}
165+
166+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
167+
const additionalPageData = example.AdditionalPage.find(p => p.Name === 'create_web_form');
168+
return res.render('pages/webforms-examples/eg001WebFormCreate', {
169+
eg: eg,
170+
csrfToken: req.csrfToken(),
171+
title: example.ExampleName,
172+
description: additionalPageData.ResultsPageText,
173+
example: example,
174+
});
175+
};

lib/webforms/controllers/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports.eg001webforms = require('./eg001CreateInstance');

0 commit comments

Comments
 (0)