|
1 | | -/** |
2 | | - * @file |
3 | | - * Example 007: Get a document from an envelope |
4 | | - * @author DocuSign |
5 | | - */ |
6 | | - |
7 | | -const path = require('path') |
8 | | - , docusign = require('docusign-esign') |
9 | | - , dsConfig = require('../../ds_configuration.js').config |
10 | | - , validator = require('validator') |
11 | | - , stream = require('stream') |
12 | | - , {promisify} = require('util') // http://2ality.com/2017/05/util-promisify.html |
13 | | - ; |
14 | | - |
15 | | -const eg007EnvelopeGetDoc = exports |
16 | | - , eg = 'eg007' // This example reference. |
17 | | - , mustAuthenticate = '/ds/mustAuthenticate' |
18 | | - , minimumBufferMin = 3 |
19 | | - ; |
20 | | - |
21 | | -/** |
22 | | - * Form page for this application |
23 | | - */ |
24 | | -eg007EnvelopeGetDoc.getController = (req, res) => { |
25 | | - // Check that the authentication token is ok with a long buffer time. |
26 | | - // If needed, now is the best time to ask the user to authenticate |
27 | | - // since they have not yet entered any information into the form. |
28 | | - let tokenOK = req.dsAuthCodeGrant.checkToken(); |
29 | | - if (tokenOK) { |
30 | | - let envelopeDocuments = req.session.envelopeDocuments, |
31 | | - documentOptions; |
32 | | - if (envelopeDocuments) { |
33 | | - // Prepare the select items |
34 | | - documentOptions = envelopeDocuments.documents.map ( item => |
35 | | - ({text: item.name, documentId: item.documentId})); |
36 | | - } |
37 | | - res.render('pages/examples/eg007EnvelopeGetDoc', { |
38 | | - csrfToken: req.csrfToken(), |
39 | | - title: "Download a document", |
40 | | - envelopeOk: req.session.envelopeId, |
41 | | - documentsOk: envelopeDocuments, |
42 | | - documentOptions: documentOptions, |
43 | | - sourceFile: path.basename(__filename), |
44 | | - sourceUrl: dsConfig.githubExampleUrl + path.basename(__filename), |
45 | | - documentation: dsConfig.documentation + eg, |
46 | | - showDoc: dsConfig.documentation |
47 | | - }); |
48 | | - } else { |
49 | | - // Save the current operation so it will be resumed after authentication |
50 | | - req.dsAuthCodeGrant.setEg(req, eg); |
51 | | - res.redirect(mustAuthenticate); |
52 | | - } |
53 | | -} |
54 | | - |
55 | | -/** |
56 | | - * Get the envelope |
57 | | - * @param {object} req Request obj |
58 | | - * @param {object} res Response obj |
59 | | - */ |
60 | | -eg007EnvelopeGetDoc.createController = async (req, res) => { |
61 | | - // Step 1. Check the token |
62 | | - // At this point we should have a good token. But we |
63 | | - // double-check here to enable a better UX to the user. |
64 | | - let tokenOK = req.dsAuthCodeGrant.checkToken(minimumBufferMin); |
65 | | - if (! tokenOK) { |
66 | | - req.flash('info', 'Sorry, you need to re-authenticate.'); |
67 | | - // We could store the parameters of the requested operation |
68 | | - // so it could be restarted automatically. |
69 | | - // But since it should be rare to have a token issue here, |
70 | | - // we'll make the user re-enter the form data after |
71 | | - // authentication. |
72 | | - req.dsAuthCodeGrant.setEg(req, eg); |
73 | | - res.redirect(mustAuthenticate); |
74 | | - } |
75 | | - let envelopeDocuments = req.session.envelopeDocuments; |
76 | | - if (! req.session.envelopeId || ! envelopeDocuments ) { |
77 | | - res.render('pages/examples/eg007EnvelopeGetDoc', { |
78 | | - csrfToken: req.csrfToken(), |
79 | | - title: "Download a document", |
80 | | - envelopeOk: req.session.envelopeId, |
81 | | - documentsOk: envelopeDocuments, |
82 | | - sourceFile: path.basename(__filename), |
83 | | - sourceUrl: dsConfig.githubExampleUrl + path.basename(__filename), |
84 | | - documentation: dsConfig.documentation + eg, |
85 | | - showDoc: dsConfig.documentation |
86 | | - }); |
87 | | - } |
88 | | - |
89 | | - // Step 2. Call the worker method |
90 | | - let accountId = req.dsAuthCodeGrant.getAccountId() |
91 | | - , dsAPIclient = req.dsAuthCodeGrant.getDSApi() |
92 | | - // Additional data validation might also be appropriate |
93 | | - , documentId = validator.escape(req.body.docSelect) |
94 | | - , args = { |
95 | | - dsAPIclient: dsAPIclient, |
96 | | - accountId: accountId, |
97 | | - documentId: documentId, |
98 | | - envelopeDocuments: envelopeDocuments |
| 1 | +"""007: Get an envelope's document""" |
| 2 | + |
| 3 | +from flask import render_template, url_for, redirect, session, flash, request, send_file |
| 4 | +from os import path |
| 5 | +import json |
| 6 | +import re |
| 7 | +import io |
| 8 | +from app import app, ds_config, views |
| 9 | +from docusign_esign import * |
| 10 | +from docusign_esign.rest import ApiException |
| 11 | + |
| 12 | +eg = "eg007" # reference (and url) for this example |
| 13 | + |
| 14 | +def controller(): |
| 15 | + """Controller router using the HTTP method""" |
| 16 | + if request.method == 'GET': |
| 17 | + return get_controller() |
| 18 | + elif request.method == 'POST': |
| 19 | + return create_controller() |
| 20 | + else: |
| 21 | + return render_template('404.html'), 404 |
| 22 | + |
| 23 | + |
| 24 | +def create_controller(): |
| 25 | + """ |
| 26 | + 1. Check the token |
| 27 | + 2. Call the worker method |
| 28 | + 3. Show results |
| 29 | + """ |
| 30 | + minimum_buffer_min = 3 |
| 31 | + token_ok = views.ds_token_ok(minimum_buffer_min) |
| 32 | + if token_ok and 'envelope_id' in session and 'envelope_documents' in session: |
| 33 | + # 2. Call the worker method |
| 34 | + # More data validation would be a good idea here |
| 35 | + # Strip anything other than characters listed |
| 36 | + pattern = re.compile('([^\w \-\@\.\,])+') |
| 37 | + document_id = pattern.sub('', request.form.get('document_id')) |
| 38 | + |
| 39 | + args = { |
| 40 | + 'account_id': session['ds_account_id'], |
| 41 | + 'envelope_id': session['envelope_id'], |
| 42 | + 'base_path': session['ds_base_path'], |
| 43 | + 'ds_access_token': session['ds_access_token'], |
| 44 | + 'document_id': document_id, |
| 45 | + 'envelope_documents': session['envelope_documents'] |
99 | 46 | } |
100 | | - , results = null |
101 | | - ; |
102 | | - |
103 | | - try { |
104 | | - results = await eg007EnvelopeGetDoc.worker (args) |
105 | | - } |
106 | | - catch (error) { |
107 | | - let errorBody = error && error.response && error.response.body |
108 | | - // we can pull the DocuSign error code and message from the response body |
109 | | - , errorCode = errorBody && errorBody.errorCode |
110 | | - , errorMessage = errorBody && errorBody.message |
111 | | - ; |
112 | | - // In production, may want to provide customized error messages and |
113 | | - // remediation advice to the user. |
114 | | - res.render('pages/error', {err: error, errorCode: errorCode, errorMessage: errorMessage}); |
115 | | - } |
116 | | - if (results) { |
117 | | - // ***DS.snippet.2.start |
118 | | - res.writeHead(200, { |
119 | | - 'Content-Type': results.mimetype, |
120 | | - 'Content-disposition': 'inline;filename=' + results.docName, |
121 | | - 'Content-Length': results.fileBytes.length |
122 | | - }); |
123 | | - res.end(results.fileBytes, 'binary'); |
124 | | - // ***DS.snippet.2.end |
125 | | - } |
126 | | -} |
127 | | - |
128 | | -/** |
129 | | - * This function does the work of listing the envelope's recipients |
130 | | - * @param {object} args An object with the following elements: <br/> |
131 | | - * <tt>dsAPIclient</tt>: The DocuSign API Client object, already set with an access token and base url <br/> |
132 | | - * <tt>accountId</tt>: Current account Id <br/> |
133 | | - * <tt>documentId</tt>: the document to be fetched <br/> |
134 | | - * <tt>envelopeDocuments</tt>: object with data about the envelope's documents |
135 | | - */ |
136 | | -// ***DS.worker.start ***DS.snippet.1.start |
137 | | -eg007EnvelopeGetDoc.worker = async (args) => { |
138 | | - let envelopesApi = new docusign.EnvelopesApi(args.dsAPIclient) |
139 | | - , getEnvelopeDocumentP = promisify(envelopesApi.getDocument).bind(envelopesApi) |
140 | | - , results = null |
141 | | - ; |
142 | | - |
143 | | - // Step 1. EnvelopeDocuments::get. |
144 | | - // Exceptions will be caught by the calling function |
145 | | - results = await getEnvelopeDocumentP( |
146 | | - args.accountId, args.envelopeDocuments.envelopeId, args.documentId, null); |
147 | | - |
148 | | - let docItem = args.envelopeDocuments.documents.find(item => item.documentId === args.documentId) |
149 | | - , docName = docItem.name |
150 | | - , hasPDFsuffix = docName.substr(docName.length - 4).toUpperCase() === '.PDF' |
151 | | - , pdfFile = hasPDFsuffix |
152 | | - ; |
153 | | - // Add .pdf if it's a content or summary doc and doesn't already end in .pdf |
154 | | - if ((docItem.type === "content" || docItem.type === "summary") && !hasPDFsuffix){ |
155 | | - docName += ".pdf"; |
156 | | - pdfFile = true; |
157 | | - } |
158 | | - // Add .zip as appropriate |
159 | | - if (docItem.type === "zip") { |
160 | | - docName += ".zip" |
161 | | - } |
162 | | - |
163 | | - // Return the file information |
164 | | - // See https://stackoverflow.com/a/30625085/64904 |
165 | | - let mimetype; |
166 | | - if (pdfFile) { |
| 47 | + |
| 48 | + try: |
| 49 | + results = worker(args) |
| 50 | + except ApiException as err: |
| 51 | + error_body_json = err and hasattr(err, 'body') and err.body |
| 52 | + # we can pull the DocuSign error code and message from the response body |
| 53 | + error_body = json.loads(error_body_json) |
| 54 | + error_code = error_body and 'errorCode' in error_body and error_body['errorCode'] |
| 55 | + error_message = error_body and 'message' in error_body and error_body['message'] |
| 56 | + # In production, may want to provide customized error messages and |
| 57 | + # remediation advice to the user. |
| 58 | + return render_template('error.html', |
| 59 | + err=err, |
| 60 | + error_code=error_code, |
| 61 | + error_message=error_message |
| 62 | + ) |
| 63 | + |
| 64 | + return send_file( |
| 65 | + results["data"], |
| 66 | + mimetype=results["mimetype"], |
| 67 | + as_attachment=True, |
| 68 | + attachment_filename=results["doc_name"] |
| 69 | + ) |
| 70 | + |
| 71 | + elif not token_ok: |
| 72 | + flash('Sorry, you need to re-authenticate.') |
| 73 | + # We could store the parameters of the requested operation |
| 74 | + # so it could be restarted automatically. |
| 75 | + # But since it should be rare to have a token issue here, |
| 76 | + # we'll make the user re-enter the form data after |
| 77 | + # authentication. |
| 78 | + session['eg'] = url_for(eg) |
| 79 | + return redirect(url_for('ds_must_authenticate')) |
| 80 | + elif not 'envelope_id' in session or not 'envelope_documents' in session: |
| 81 | + return render_template("eg007_envelope_get_doc.html", |
| 82 | + title="Download an Envelope's Document", |
| 83 | + envelope_ok=False, |
| 84 | + documents_ok=False, |
| 85 | + source_file=path.basename(__file__), |
| 86 | + source_url=ds_config.DS_CONFIG['github_example_url'] + path.basename(__file__), |
| 87 | + documentation=ds_config.DS_CONFIG['documentation'] + eg, |
| 88 | + show_doc=ds_config.DS_CONFIG['documentation'], |
| 89 | + ) |
| 90 | + |
| 91 | + |
| 92 | +def worker(args): |
| 93 | + """ |
| 94 | + 1. Call the envelope get method |
| 95 | + """ |
| 96 | + |
| 97 | + # Exceptions will be caught by the calling function |
| 98 | + api_client = ApiClient() |
| 99 | + api_client.host = args['base_path'] |
| 100 | + api_client.set_default_header("Authorization", "Bearer " + args['ds_access_token']) |
| 101 | + envelope_api = EnvelopesApi(api_client) |
| 102 | + document_id = args['document_id'] |
| 103 | + |
| 104 | + # The SDK always stores the received file as a temp file |
| 105 | + temp_file = envelope_api.get_document(args['account_id'], document_id, args['envelope_id']) |
| 106 | + doc_item = next(item for item in args['envelope_documents']['documents'] if item['document_id'] == document_id) |
| 107 | + doc_name = doc_item['name'] |
| 108 | + has_pdf_suffix = doc_name[-4:].upper() == '.PDF' |
| 109 | + pdf_file = has_pdf_suffix |
| 110 | + # Add .pdf if it's a content or summary doc and doesn't already end in .pdf |
| 111 | + if (doc_item["type"] == "content" or doc_item["type"] == "summary") and not has_pdf_suffix: |
| 112 | + doc_name += ".pdf" |
| 113 | + pdf_file = True |
| 114 | + # Add .zip as appropriate |
| 115 | + if doc_item["type"] == "zip": |
| 116 | + doc_name += ".zip" |
| 117 | + |
| 118 | + # Return the file information |
| 119 | + if pdf_file: |
167 | 120 | mimetype = 'application/pdf' |
168 | | - } else if (docItem.type === 'zip') { |
| 121 | + elif doc_item["type"] == 'zip': |
169 | 122 | mimetype = 'application/zip' |
170 | | - } else { |
| 123 | + else: |
171 | 124 | mimetype = 'application/octet-stream' |
172 | | - } |
173 | 125 |
|
174 | | - return ({mimetype: mimetype, docName: docName, fileBytes: results}); |
175 | | -} |
176 | | -// ***DS.worker.end ***DS.snippet.1.end |
| 126 | + return {'mimetype': mimetype, 'doc_name': doc_name, 'data': temp_file} |
| 127 | + |
| 128 | + |
| 129 | +# ***DS.worker.end ***DS.snippet.1.end |
| 130 | + |
| 131 | + |
| 132 | +def get_controller(): |
| 133 | + """responds with the form for the example""" |
| 134 | + |
| 135 | + if views.ds_token_ok(): |
| 136 | + documents_ok = 'envelope_documents' in session |
| 137 | + document_options = [] |
| 138 | + if documents_ok: |
| 139 | + # Prepare the select items |
| 140 | + envelope_documents = session["envelope_documents"] |
| 141 | + document_options = map( lambda item : |
| 142 | + {'text': item['name'], 'document_id': item['document_id']} |
| 143 | + , envelope_documents['documents']) |
| 144 | + |
| 145 | + return render_template("eg007_envelope_get_doc.html", |
| 146 | + title="Download an Envelope's Document", |
| 147 | + envelope_ok='envelope_id' in session, |
| 148 | + documents_ok=documents_ok, |
| 149 | + source_file=path.basename(__file__), |
| 150 | + source_url=ds_config.DS_CONFIG['github_example_url'] + path.basename(__file__), |
| 151 | + documentation=ds_config.DS_CONFIG['documentation'] + eg, |
| 152 | + show_doc=ds_config.DS_CONFIG['documentation'], |
| 153 | + document_options=document_options |
| 154 | + ) |
| 155 | + else: |
| 156 | + # Save the current operation so it will be resumed after authentication |
| 157 | + session['eg'] = url_for(eg) |
| 158 | + return redirect(url_for('ds_must_authenticate')) |
177 | 159 |
|
0 commit comments