Skip to content

Commit cc63386

Browse files
author
Raileen Del Rosario
committed
2 parents 9f4ee8a + d8e91da commit cc63386

26 files changed

+1311
-14
lines changed

index.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ const { eg001webforms } = require('./lib/webforms/controllers');
6060
const { eg004notary } = require('./lib/notary/controllers');
6161
const { eg001fields } = require('./lib/connectedFields/controllers');
6262
const { eg001Navigator, eg002Navigator } = require('./lib/navigator/controllers');
63+
const { eg001maestro, eg002maestro, eg003maestro, eg004maestro } = require('./lib/maestro/controllers');
6364

6465
const PORT = process.env.PORT || 3000;
6566
const HOST = process.env.HOST || 'localhost';
@@ -299,6 +300,16 @@ app.get('/nav001', eg001Navigator.getController)
299300
.get('/nav002', eg002Navigator.getController)
300301
.post('/nav002', eg002Navigator.createController);
301302

303+
app.get('/mae001', eg001maestro.getController)
304+
.post('/mae001', eg001maestro.createController)
305+
.post('/mae001publish', eg001maestro.publishController)
306+
.get('/mae002', eg002maestro.getController)
307+
.post('/mae002', eg002maestro.createController)
308+
.get('/mae003', eg003maestro.getController)
309+
.post('/mae003', eg003maestro.createController)
310+
.get('/mae004', eg004maestro.getController)
311+
.post('/mae004', eg004maestro.createController);
312+
302313
function dsLoginCB1(req, res, next) { req.dsAuthCodeGrant.oauth_callback1(req, res, next); }
303314
function dsLoginCB2(req, res, next) { req.dsAuthCodeGrant.oauth_callback2(req, res, next); }
304315

@@ -352,6 +363,9 @@ const NOTARY_SCOPES = [
352363
const CONNECTED_FIELDS_SCOPES = [
353364
'signature', 'adm_store_unified_repo_read'
354365
];
366+
const MAESTRO_SCOPES = [
367+
'signature', 'aow_manage'
368+
];
355369

356370
const scope = [
357371
...ROOM_SCOPES,
@@ -362,6 +376,7 @@ const scope = [
362376
...WEBFORMS_SCOPES,
363377
...NOTARY_SCOPES,
364378
...CONNECTED_FIELDS_SCOPES,
379+
...MAESTRO_SCOPES
365380
];
366381

367382
// Configure passport for DocusignStrategy

lib/connectedFields/controllers/eg001SetConnectedFields.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,10 @@ eg001SetConnectedFields.createController = async (req, res) => {
102102

103103
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
104104
if (tabGroups.length === 0) {
105-
const additionalPageData = example.AdditionalPage.filter(p => p.Name === 'no_verification_app')[0];
106-
107-
return res.render('pages/example_done', {
108-
title: example.ExampleName,
109-
message: additionalPageData?.ResultsPageText,
110-
});
105+
const errorCode = '404';
106+
const errorMessage = 'No data verification extension apps found';
107+
const errorInfo = example.CustomErrorTexts[0].ErrorMessage;
108+
return res.render('pages/error', { errorCode, errorMessage, errorInfo });
111109
}
112110
req.session.apps = tabGroups;
113111

lib/connectedFields/examples/setConnectedFields.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const iam = require('@docusign/iam-sdk');
1414
*/
1515
//ds-snippet-start:ConnectedFields1Step2
1616
const getTabGroups = async (args) => {
17-
const client = new iam.IamClient({ accessToken: args.accessToken, serverURL: args.basePath });
17+
const client = new iam.IamClient({ accessToken: args.accessToken });
1818
return await client.connectedFields.tabInfo.getConnectedFieldsTabGroups({ accountId: args.accountId });
1919
};
2020
//ds-snippet-end:ConnectedFields1Step2
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/**
2+
* @file
3+
* Example 001: How to trigger a Maestro workflow
4+
* @author DocuSign
5+
*/
6+
7+
const path = require('path');
8+
const validator = require('validator');
9+
const { formatString, API_TYPES } = require('../../utils.js');
10+
const { getExampleByNumber } = require('../../manifestService');
11+
const dsConfig = require('../../../config/index.js').config;
12+
const { getMaestroWorkflows, triggerWorkflow } = require('../examples/triggerWorkflow');
13+
const { createWorkflow, publishWorkflow } = require('../workflowUtils.js');
14+
15+
const eg001TriggerWorkflow = exports;
16+
const exampleNumber = 1;
17+
const eg = `mae00${exampleNumber}`; // This example reference.
18+
const api = API_TYPES.MAESTRO;
19+
const mustAuthenticate = '/ds/mustAuthenticate';
20+
const minimumBufferMin = 3;
21+
const workflowName = 'Example workflow - send invite to signer';
22+
23+
24+
/**
25+
* Trigger workflow
26+
* @param {object} req Request obj
27+
* @param {object} res Response obj
28+
*/
29+
eg001TriggerWorkflow.createController = async (req, res) => {
30+
// Step 1. Check the token
31+
// At this point we should have a good token. But we
32+
// double-check here to enable a better UX to the user.
33+
const isTokenOK = req.dsAuth.checkToken(minimumBufferMin);
34+
if (!isTokenOK) {
35+
req.flash('info', 'Sorry, you need to re-authenticate.');
36+
// Save the current operation so it will be resumed after authentication
37+
req.dsAuth.setEg(req, eg);
38+
return res.redirect(mustAuthenticate);
39+
}
40+
41+
// Step 2. Call the worker method
42+
const { body } = req;
43+
const args = {
44+
instanceName: validator.escape(body.instanceName),
45+
signerEmail: validator.escape(body.signerEmail),
46+
signerName: validator.escape(body.signerName),
47+
ccEmail: validator.escape(body.ccEmail),
48+
ccName: validator.escape(body.ccName),
49+
accessToken: req.user.accessToken,
50+
basePath: dsConfig.maestroApiUrl,
51+
accountId: req.session.accountId,
52+
};
53+
54+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
55+
try {
56+
const { instanceUrl, instanceId } = await triggerWorkflow(args, req.session.workflowId);
57+
req.session.instanceId = instanceId;
58+
59+
return res.render('pages/maestro-examples/eg001EmbedWorkflow', {
60+
title: example.ExampleName,
61+
message: formatString(example.ResultsPageText, JSON.stringify(instanceId)),
62+
instanceUrl,
63+
});
64+
} catch (error) {
65+
const errorCode = error?.response?.statusCode;
66+
const errorMessage = error?.response?.body?.message;
67+
return res.render('pages/error', { err: error, errorCode, errorMessage });
68+
}
69+
};
70+
71+
/**
72+
* Form page for this application
73+
*/
74+
eg001TriggerWorkflow.getController = async (req, res) => {
75+
// Check that the authentication token is ok with a long buffer time.
76+
// If needed, now is the best time to ask the user to authenticate
77+
// since they have not yet entered any information into the form.
78+
const isTokenOK = req.dsAuth.checkToken();
79+
if (!isTokenOK) {
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+
const args = {
86+
accessToken: req.user.accessToken,
87+
basePath: dsConfig.maestroApiUrl,
88+
accountId: req.session.accountId,
89+
};
90+
91+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
92+
const sourceFile =
93+
path.basename(__filename)[5].toLowerCase() +
94+
path.basename(__filename).substr(6);
95+
96+
try {
97+
const workflows = await getMaestroWorkflows(args);
98+
if (!workflows.data || workflows.data.length === 0 || !workflows.data.find(wf => wf.name === workflowName)) {
99+
if (!req.session.templateId) {
100+
return res.render('pages/maestro-examples/eg001TriggerWorkflow', {
101+
eg: eg,
102+
csrfToken: req.csrfToken(),
103+
example: example,
104+
templateOk: false,
105+
sourceFile: sourceFile,
106+
sourceUrl: dsConfig.githubExampleUrl + 'maestro/examples/' + sourceFile,
107+
documentation: dsConfig.documentation + eg,
108+
showDoc: dsConfig.documentation,
109+
});
110+
}
111+
112+
const newWorkflow = await createWorkflow(args, req.session.templateId);
113+
const consentUrl = await publishWorkflow(args, newWorkflow.workflowDefinitionId);
114+
115+
req.session.workflowId = newWorkflow.workflowDefinitionId;
116+
117+
if (consentUrl) {
118+
return res.render('pages/maestro-examples/eg001PublishWorkflow', {
119+
eg: eg,
120+
csrfToken: req.csrfToken(),
121+
example: example,
122+
message: example.AdditionalPage[0].ResultsPageText,
123+
consentUrl
124+
});
125+
}
126+
}
127+
128+
const workflow = workflows.data.find(wf => wf.name === workflowName);
129+
req.session.workflowId = workflow.id;
130+
} catch (error) {
131+
const errorCode = error?.response?.statusCode;
132+
const errorMessage = error?.response?.body?.message;
133+
return res.render('pages/error', { err: error, errorCode, errorMessage });
134+
}
135+
136+
res.render('pages/maestro-examples/eg001TriggerWorkflow', {
137+
eg: eg,
138+
csrfToken: req.csrfToken(),
139+
example: example,
140+
sourceFile: sourceFile,
141+
templateOk: true,
142+
sourceUrl: dsConfig.githubExampleUrl + 'maestro/examples/' + sourceFile,
143+
documentation: dsConfig.documentation + eg,
144+
showDoc: dsConfig.documentation,
145+
});
146+
};
147+
148+
/**
149+
* Publish workflow
150+
*/
151+
eg001TriggerWorkflow.publishController = async (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+
const args = {
163+
accessToken: req.user.accessToken,
164+
basePath: dsConfig.maestroApiUrl,
165+
accountId: req.session.accountId,
166+
};
167+
168+
try {
169+
const consentUrl = await publishWorkflow(args, req.session.workflowId);
170+
171+
if (consentUrl) {
172+
return res.render('pages/maestro-examples/eg001PublishWorkflow', {
173+
eg: eg,
174+
csrfToken: req.csrfToken(),
175+
example: example,
176+
message: example.AdditionalPage[0].ResultsPageText,
177+
consentUrl
178+
});
179+
}
180+
} catch (error) {
181+
const errorCode = error?.response?.statusCode;
182+
const errorMessage = error?.response?.body?.message;
183+
return res.render('pages/error', { err: error, errorCode, errorMessage });
184+
}
185+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
186+
const sourceFile =
187+
path.basename(__filename)[5].toLowerCase() +
188+
path.basename(__filename).substr(6);
189+
190+
res.render('pages/maestro-examples/eg001TriggerWorkflow', {
191+
eg: eg,
192+
csrfToken: req.csrfToken(),
193+
example: example,
194+
sourceFile: sourceFile,
195+
templateOk: true,
196+
sourceUrl: dsConfig.githubExampleUrl + 'maestro/examples/' + sourceFile,
197+
documentation: dsConfig.documentation + eg,
198+
showDoc: dsConfig.documentation,
199+
});
200+
};
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* @file
3+
* Example 002: How to pause a Maestro workflow
4+
* @author DocuSign
5+
*/
6+
7+
const path = require('path');
8+
const { formatString, API_TYPES } = require('../../utils.js');
9+
const { getExampleByNumber } = require('../../manifestService');
10+
const dsConfig = require('../../../config/index.js').config;
11+
const { pauseWorkflow } = require('../examples/pauseWorkflow');
12+
13+
const eg002PauseWorkflow = exports;
14+
const exampleNumber = 2;
15+
const eg = `mae00${exampleNumber}`; // This example reference.
16+
const api = API_TYPES.MAESTRO;
17+
const mustAuthenticate = '/ds/mustAuthenticate';
18+
const minimumBufferMin = 3;
19+
20+
/**
21+
* Trigger workflow
22+
* @param {object} req Request obj
23+
* @param {object} res Response obj
24+
*/
25+
eg002PauseWorkflow.createController = async (req, res) => {
26+
// Step 1. Check the token
27+
// At this point we should have a good token. But we
28+
// double-check here to enable a better UX to the user.
29+
const isTokenOK = req.dsAuth.checkToken(minimumBufferMin);
30+
if (!isTokenOK) {
31+
req.flash('info', 'Sorry, you need to re-authenticate.');
32+
// Save the current operation so it will be resumed after authentication
33+
req.dsAuth.setEg(req, eg);
34+
return res.redirect(mustAuthenticate);
35+
}
36+
37+
// Step 2. Call the worker method
38+
const args = {
39+
workflowId: req.session.workflowId,
40+
instanceId: req.session.instanceId,
41+
accessToken: req.user.accessToken,
42+
basePath: dsConfig.maestroApiUrl,
43+
accountId: req.session.accountId,
44+
};
45+
46+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
47+
try {
48+
const result = await pauseWorkflow(args);
49+
50+
res.render('pages/example_done', {
51+
title: example.ExampleName,
52+
message: formatString(example.ResultsPageText, req.session.instanceId),
53+
json: JSON.stringify(result)
54+
});
55+
} catch (error) {
56+
const errorCode = error?.response?.statusCode;
57+
const errorMessage = error?.response?.body?.message;
58+
const errorInfo = formatString(example.CustomErrorTexts[1].ErrorMessage, req.session.workflowId);
59+
60+
return res.render('pages/error', { err: error, errorCode, errorMessage, errorInfo });
61+
}
62+
};
63+
64+
/**
65+
* Form page for this application
66+
*/
67+
eg002PauseWorkflow.getController = async (req, res) => {
68+
// Check that the authentication token is ok with a long buffer time.
69+
// If needed, now is the best time to ask the user to authenticate
70+
// since they have not yet entered any information into the form.
71+
const isTokenOK = req.dsAuth.checkToken();
72+
if (!isTokenOK) {
73+
// Save the current operation so it will be resumed after authentication
74+
req.dsAuth.setEg(req, eg);
75+
return res.redirect(mustAuthenticate);
76+
}
77+
78+
const example = getExampleByNumber(res.locals.manifest, exampleNumber, api);
79+
if (!req.session.workflowId) {
80+
const errorCode = '404';
81+
const errorMessage = 'No workflow ID found';
82+
const errorInfo = example.CustomErrorTexts[0].ErrorMessage;
83+
return res.render('pages/error', { errorCode, errorMessage, errorInfo });
84+
}
85+
86+
const sourceFile =
87+
path.basename(__filename)[5].toLowerCase() +
88+
path.basename(__filename).substr(6);
89+
90+
res.render('pages/maestro-examples/eg002PauseWorkflow', {
91+
eg: eg,
92+
csrfToken: req.csrfToken(),
93+
example: example,
94+
sourceFile: sourceFile,
95+
sourceUrl: dsConfig.githubExampleUrl + 'maestro/examples/' + sourceFile,
96+
documentation: dsConfig.documentation + eg,
97+
showDoc: dsConfig.documentation,
98+
});
99+
};

0 commit comments

Comments
 (0)