| 
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