diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml new file mode 100644 index 00000000..c2940bc4 --- /dev/null +++ b/.github/workflows/python.yml @@ -0,0 +1,47 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: Lint with flake8 + run: flake8 + + - name: Write private key + run: | + echo '${{ secrets.PRIVATE_KEY }}' >> app/private.key + cp app/ds_config_sample.py app/ds_config.py + + - name: Test with pytest + run: pytest + env: + CLIENT_ID: '${{ secrets.CLIENT_ID }}' + USER_ID: '${{ secrets.USER_ID }}' + SIGNER_EMAIL: '${{ secrets.SIGNER_EMAIL }}' + SIGNER_NAME: '${{ secrets.SIGNER_NAME }}' diff --git a/.gitignore b/.gitignore index f9991199..101b3e4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -app/ds_config* +app/ds_config.py */.DS_Store .DS_Store @@ -119,3 +119,10 @@ fabric.properties # Security key files private.key + +# Current flask session + +flask_session/ + +# Workflow ID file +WORKFLOW_ID.txt \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..0f292b31 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +# start by pulling the python image +FROM python:3.11.9-slim-bullseye + +# copy the requirements file into the image +COPY ./requirements.txt /app/requirements.txt + +# switch working directory +WORKDIR /app + +# install the dependencies and packages in the requirements file +RUN pip install -r requirements.txt + +# copy every content from the local file to the image +COPY . /app + +# configure the container to run in an executed manner +ENTRYPOINT [ "python" ] + +CMD ["run.py", "--docker"] diff --git a/PAYMENTS_INSTALLATION.md b/PAYMENTS_INSTALLATION.md index bfd85dcb..de789f93 100644 --- a/PAYMENTS_INSTALLATION.md +++ b/PAYMENTS_INSTALLATION.md @@ -1,24 +1,21 @@ -# Configuring a DocuSign payments gateway +# Configure a payment gateway -DocuSign offers built-in connections to multiple payment -gateways. The payments example uses a demo account via the Stripe -gateway service. +DocuSign offers built-in connections to multiple payment gateways. The payments code example uses a developer account via the Stripe gateway service. -## Creating the payments gateway account -1. Login to demo.docusign.net and go to the Admin Tool. -1. On the Integrations / Payments screen, click Stripe. -1. For development, you can skip the Stripe account application - by using the `Skip this account form` link: +## Create a Stripe payment gateway + +1. Select the Stripe button on the [**Payments**](https://admindemo.docusign.com/authenticate?goTo=payments) page in your developer account. + +1. For development, you can skip the Stripe account application by using the **Skip this account form** link at the top of the page.
+ + ![Skipping the Stripe account form](docs/stripe_skip_account_form_link.png) + + An enabled Stripe payment gateway is now associated with your Docusign developer account and is shown under **Payment Gateway**. + +1. Save the **Gateway Account ID** GUID to the code example launcher configuration file. - ![Skipping the Stripe account form](docs/stripe_skip_account_form_link.png) -1. Next, the Admin Tool will show that an enabled Stripe - payment gateway account has been associated with your - DocuSign Developer account. -1. Configure the example launcher with the gateway account id shown in the Admin tool. ## Additional documentation -See the -[Managing Payment Gateways](https://support.docusign.com/en/guides/managing-payment-gateways) -documentation. - +* [Managing Payment Gateways](https://support.docusign.com/en/guides/managing-payment-gateways) +* [How to send a request for payment](https://developers.docusign.com/docs/esign-rest-api/how-to/request-a-payment) diff --git a/Pipfile.lock b/Pipfile.lock index 1fc79f49..983cd83b 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -18,10 +18,11 @@ "default": { "certifi": { "hashes": [ - "sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", - "sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae" + "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", + "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" ], - "version": "==2019.3.9" + "index": "pypi", + "version": "==2022.12.7" }, "chardet": { "hashes": [ @@ -47,10 +48,11 @@ }, "urllib3": { "hashes": [ - "sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0", - "sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3" + "sha256:2393a695cd12afedd0dcb26fe5d50d0cf248e5a66f75dbd89a3d4eb333a61af4", + "sha256:a637e5fae88995b256e3409dc4d52c2e2e0ba32c42a6365fee8bbd2238de3cfb" ], - "version": "==1.24.2" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' and python_version < '4'", + "version": "==1.24.3" } }, "develop": {} diff --git a/README.md b/README.md index fa4497da..dfe22f92 100644 --- a/README.md +++ b/README.md @@ -1,290 +1,203 @@ # Python Launcher Code Examples -### Github repo: [code-examples-python](./) +> +>### PLEASE! Share your feedback in a [two-question survey](https://docs.google.com/forms/d/e/1FAIpQLScPa74hwhJwi7XWDDj4-XZVOQTF9jJWgbIFEpulXokCqYWT4A/viewform?usp=pp_url&entry.680551577=Python). +> +> +### GitHub repo: [code-examples-python](./README.md) -This GitHub repo includes code examples for DocuSign APIs. +If you downloaded this project using the [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) tool, it may be configured in one of three ways: -To switch between API code examples, modify the EXAMPLES_API_TYPE setting at the end of the configuration file. Set only one API type to true and set the remaining to false. +* **[JWT Grant remote signing example](#jwt-grant-remote-signing-example)**–demonstrates how to implement JSON Web Token authentication. It includes a single remote signing workflow. +* **[Authorization Code Grant embedded signing example](#authorization-code-grant-embedded-signing-example)**–demonstrates how to implement Authorization Code Grant authentication. It includes a single embedded signing workflow. +* **[Multiple code examples, Authorization Code Grant and JWT Grant](#installation-steps)**–includes the full range of examples and authentication types. + +***Installation and running instructions vary depending on the configuration. Follow the link that matches your project type to get started.*** + +This GitHub repo includes code examples for the [Web Forms API](https://developers.docusign.com/docs/web-forms-api/), [Docusign Admin API](https://developers.docusign.com/docs/admin-api/), [Click API](https://developers.docusign.com/docs/click-api/), [eSignature REST API](https://developers.docusign.com/docs/esign-rest-api/), [Monitor API](https://developers.docusign.com/docs/monitor-api/), and [Rooms API](https://developers.docusign.com/docs/rooms-api/). -If none of the API types are set to true, the DocuSign eSignature REST API code examples will be shown. If multiple API types are set to true, only the first will be shown. -**Note:** to use the Rooms API you must also [create your DocuSign Developer Account for Rooms](https://developers.docusign.com/docs/rooms-api/rooms101/create-account). ## Introduction -This repo is a Python 3 application that demonstrates: + +This repo is a Python 3 application that supports the following authentication workflows: + +* Authentication with Docusign via [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode). +When the token expires, the user is asked to re-authenticate. The refresh token is not used. + +* Authentication with Docusign via [JSON Web Token (JWT) Grant](https://developers.docusign.com/platform/auth/jwt/). +When the token expires, it updates automatically. ## eSignature API -1. **Use embedded signing.** - [Source.](./app/eg001_embedded_signing/controller.py) - This example sends an envelope, and then uses embedded signing for the first signer. - With embedded signing, the DocuSign signing is initiated from your website. -1. **Request a signature by email (Remote Signing).** - [Source.](./app/eSignature/examples/eg002_signing_via_email/controller.py) - The envelope includes a pdf, Word, and HTML document. - Anchor text ([AutoPlace](https://support.docusign.com/en/guides/AutoPlace-New-DocuSign-Experience)) is used to position the signing fields in the documents. -1. **List envelopes in the user's account.** - [Source.](./app/eSignature/examples/eg003_list_envelopes/controller.py) - The envelopes' current status is included. -1. **Get an envelope's basic information.** - [Source.](./app/eSignature/examples/eg004_envelope_info/controller.py) - The example lists the basic information about an envelope, including its overall status. -1. **List an envelope's recipients** - [Source.](./app/eSignature/examples/eg005_envelope_recipients/controller.py) - Includes current recipient status. -1. **List an envelope's documents.** - [Source.](./app/eSignature/examples/eg006_envelope_docs/controller.py) -1. **Download an envelope's documents.** - [Source.](./app/eSignature/examples/eg007_envelope_get_doc/controller.py) - The example can download individual - documents, the documents concatenated together, or a zip file of the documents. -1. **Programmatically create a template.** - [Source.](./app/eSignature/examples/eg008_create_template/controller.py) -1. **Request a signature by email using a template.** - [Source.](./app/eSignature/examples/eg009_use_template/controller.py) -1. **Send an envelope and upload its documents with multipart binary transfer.** - [Source.](./app/eSignature/examples/eg010_send_binary_docs/controller.py) - Binary transfer is 33% more efficient than using Base64 encoding. -1. **Use embedded sending.** - [Source.](./app/eSignature/examples/eg011_embedded_sending/controller.py) - Embeds the DocuSign web tool (NDSE) in your web app to finalize or update - the envelope and documents before they are sent. -1. **Embedded DocuSign web tool (NDSE).** - [Source.](./app/eSignature/examples/eg012_embedded_console/controller.py) -1. **Use embedded signing from a template with an added document.** - [Source.](./app/eSignature/examples/eg013_add_doc_to_template/controller.py) - This example sends an envelope based on a template. - In addition to the template's document(s), the example adds an - additional document to the envelope by using the - [Composite Templates](https://developers.docusign.com/esign-rest-api/guides/features/templates#composite-templates) - feature. -1. **Payments example: an order form, with online payment by credit card.** - [Source.](./app/eSignature/examples/eg014_collect_payment/controller.py) -1. **Get the envelope tab data.** - Retrieve the tab (field) values for all of the envelope's recipients. - [Source.](./app/eSignature/examples/eg015_envelope_tab_data/controller.py) -1. **Set envelope tab values.** - The example creates an envelope and sets the initial values for its tabs (fields). Some of the tabs - are set to be read-only, others can be updated by the recipient. The example also stores - metadata with the envelope. - [Source.](./app/eSignature/examples/eg016_set_tab_values/controller.py) -1. **Set template tab values.** - The example creates an envelope using a template and sets the initial values for its tabs (fields). - The example also stores metadata with the envelope. - [Source.](./app/eSignature/examples/eg017_set_template_tab_values/controller.py) -1. **Get the envelope custom field data (metadata).** - The example retrieves the custom metadata (custom data fields) stored with the envelope. - [Source.](./app/eSignature/examples/eg018_envelope_custom_field_data/controller.py) -1. **Requiring an Access Code for a Recipient** - [Source.](./app/eSignature/examples/eg019_access_code_authentication/controller.py) - This example sends an envelope that requires an access-code for the purpose of multi-factor authentication. -1. **Requiring SMS authentication for a recipient** - [Source.](./app/eSignature/examples/eg020_sms_authentication/controller.py) - This example sends an envelope that requires entering in a six digit code from an text message for the purpose of multi-factor authentication. -1. **Requiring Phone authentication for a recipient** - [Source.](./app/eSignature/examples/eg021_phone_authentication/controller.py) - This example sends an envelope that requires entering in a voice-based response code for the purpose of multi-factor authentication. -1. **Requiring Knowledge-Based Authentication (KBA) for a Recipient** - [Source.](./app/eSignature/examples/eg022_kba_authentication/controller.py) - This example sends an envelope that requires passing a Public records check to validate identity for the purpose of multi-factor authentication. -1. **Requiring ID Verification (IDV) for a recipient** - [Source.](./app/eSignature/examples/eg023_idv_authentication/controller.py) - This example sends an envelope that requires submitting a photo of a government issued id for the purpose of multi-factor authentication. -1. **Creating a permission profile** - [Source.](./app/eSignature/examples/eg024_permissions_creating/controller.py) - This code example demonstrates how to create a permission profile using the [Create Permission Profile](https://developers.docusign.com/esign-rest-api/reference/Accounts/AccountPermissionProfiles/create) method. -1. **Setting a permission profile** - [Source.](./app/eSignature/examples/eg025_permissions_set_user_group/controller.py) - This code example demonstrates how to set a user group’s permission profile using the [Update Group](https://developers.docusign.com/esign-rest-api/reference/UserGroups/Groups/update) method. - You must have already created the permissions profile and the group of users. -1. **Updating individual permission settings** - [Source.](./app/eSignature/examples/eg026_permissions_change_single_setting/controller.py) - This code example demonstrates how to edit individual permission settings on a permissions profile using the [Update Permission Profile](https://developers.docusign.com/esign-rest-api/reference/Accounts/AccountPermissionProfiles/update) method. -1. **Deleting a permission profile** - [Source.](./app/eSignature/examples/eg027_permissions_delete/controller.py) - This code example demonstrates how to delete a permission profile using the [Delete Permission Profile](https://developers.docusign.com/esign-rest-api/reference/Accounts/AccountPermissionProfiles/create) method. -1. **Creating a brand** - [Source.](./app/eSignature/examples/eg028_brand_creating/controller.py) - This example creates brand profile for an account using the [Create Brand](https://developers.docusign.com/esign-rest-api/reference/Accounts/AccountBrands/create) method. -1. **Applying a brand to an envelope** - [Source.](./app/eSignature/examples/eg029_brands_apply_to_envelope/controller.py) - This code example demonstrates how to apply a brand you've created to an envelope using the [Create Envelope](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method. - First, creates the envelope and then applies the brand to it. - Anchor text ([AutoPlace](https://support.docusign.com/en/guides/AutoPlace-New-DocuSign-Experience)) is used to position the signing fields in the documents. -1. **Applying a brand to a template** - [Source.](./app/eSignature/examples/eg030_brands_apply_to_template/controller.py) - This code example demonstrates how to apply a brand you've created to a template using the [Create Envelope](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method. - You must have already created the template and the brand. - Anchor text ([AutoPlace](https://support.docusign.com/en/guides/AutoPlace-New-DocuSign-Experience)) is used to position the signing fields in the documents. -1. **Bulk sending envelopes to multiple recipients** - [Source.](./app/eSignature/examples/eg031_bulk_send/controller.py) - This code example demonstrates how to send envelopes in bulk to multiple recipients using these methods: - [Create Bulk Send List](https://developers.docusign.com/esign-rest-api/reference/BulkEnvelopes/BulkSend/createBulkSendList), - [Create Bulk Send Request](https://developers.docusign.com/esign-rest-api/reference/BulkEnvelopes/BulkSend/createBulkSendRequest). - Firstly, creates a bulk send recipients list, and then creates an envelope. - After that, initiates bulk envelope sending. -1. **Pausing a signature workflow** - [Source.](./app/eSignature/examples/eg032_pause_signature_workflow/controller.py) - This code example demonstrates how to create an envelope where the workflow is paused before the envelope is sent to a second recipient using the [Create Envelope](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method. -1. **Unpausing a signature workflow** - [Source.](./app/eSignature/examples/eg033_unpause_signature_workflow/controller.py) - This code example demonstrates how to update an envelope to resume the workflow that has been paused using the [Update Envelope](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/update) method. - You must have created at least one envelope with a paused signature workflow to run this example. -1. **Using conditional recipients** - [Source.](./app/eSignature/examples/eg034_use_conditional_recipients/controller.py) - This code example demonstrates how to create an envelope where the workflow is routed to different recipients based on the value of a transaction using the [Create Envelope](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method. -1. **Request a signature by SMS delivery** - [Source.](./app/eSignature/examples/eg035_sms_delivery/controller.py) - This code example demonstrates how to send a signature request via an SMS message using the [Envelopes: create](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method. - - -## Rooms API -**Note:** to use the Rooms API you must also [create your DocuSign Developer Account for Rooms](https://developers.docusign.com/docs/rooms-api/rooms101/create-account). Examples 4 and 6 require that you have the [DocuSign Forms feature](https://developers.docusign.com/docs/rooms-api/rooms101/using-forms-in-a-room) enabled in your Rooms for Real Estate account. - -1. **Create a room with data.** -[Source.](./app/rooms/examples/eg001_create_room_with_data/controller.py) -This example creates a new room in your DocuSign Rooms account to be used for a transaction. -1. **Create a room from a template.** -[Source.](./app/rooms/examples/eg002_create_room_with_template/controller.py) -This example creates a new room using a template. -1. **Create a room with Data.** -[Source.](./app/rooms/examples/eg003_export_data_from_room/controller.py) -This example exports all the available data from a specific room in your DocuSign Rooms account. -1. **Add forms to a room.** -[Source.](./app/rooms/examples/eg004_add_forms_to_room/controller.py) -This example adds a standard real estate related form to a specific room in your DocuSign Rooms account. -1. **How to search for rooms with filters.** -[Source.](./app/rooms/examples/eg005_get_rooms_with_filters/controller.py) -This example demonstrates how to return rooms that have had their field data, -updated within the time period between Start date and End date. -1. **Create an external form fillable session.** -[Source.](./app/rooms/examples/eg006_create_external_form_fill_session/controller.py) -This example demonstrates how to create an -external form fill session -using the Rooms API:
-the result of this code example is the URL for the form fill session, which you can embed -in your integration or send to the user. -1. **Create a form group** -[Source.](./app/rooms/examples/eg007_create_form_group/controller.py) -This example demonstrates creating a DocuSign Form Group. -1. **Grant office access to a form group** -[Source.](./app/rooms/examples/eg008_grant_office_access_to_form_group/controller.py) -This example demonstrates how to grant Office access to a Form Group. -1. **Assign a form to a form group** -[Source.](./app/rooms/examples/eg009_assign_form_to_form_group/controller.py) -This example demonstrates how to assign a form to a form group. - -## Click API - -1. **Create a clickwrap.** -[Source.](./app/click/examples/eg001_create_clickwrap/controller.py) -This example demonstrates how to use DocuSign Click to create a clickwrap that you can embed in your website or app. -1. **Activate a clickwrap.** -[Source.](./app/click/examples/eg002_activate_clickwrap/controller.py) -This example demonstrates how to use DocuSign Click to activate a new clickwrap that you have already created. -1. **Create a new clickwrap version.** -[Source.](./app/click/examples/eg005_create_new_clickwrap_version/controller.py) -This example demonstrates how to use DocuSign Click to create a new version of a clickwrap. -1. **Get a list of clickwraps.** -This example demonstrates how to use DocuSign Click to get a list of clickwraps associated with a specific DocuSign user. -[Source.](./app/click/examples/eg006_list_clickwraps/controller.py) -1. **Get clickwrap responses.** -This example demonstrates how to use DocuSign Click to get user responses to your clickwrap agreements. -[Source.](./app/click/examples/eg007_clickwrap_responses/controller.py) +For more information about the scopes used for obtaining authorization to use the eSignature API, see [Required scopes](https://developers.docusign.com/docs/esign-rest-api/esign101/auth#required-scopes). + +For a list of code examples that use the eSignature API, see the [How-to guides overview](https://developers.docusign.com/docs/esign-rest-api/how-to/) on the Docusign Developer Center. + + +## Rooms API + +**Note:** To use the Rooms API, you must also [create your Rooms developer account](https://developers.docusign.com/docs/rooms-api/rooms101/create-account). Examples 4 and 6 require that you have the Docusign Forms feature enabled in your Rooms for Real Estate account. +For more information about the scopes used for obtaining authorization to use the Rooms API, see [Required scopes](https://developers.docusign.com/docs/rooms-api/rooms101/auth/). + +For a list of code examples that use the Rooms API, see the [How-to guides overview](https://developers.docusign.com/docs/rooms-api/how-to/) on the Docusign Developer Center. + + +## Click API +For more information about the scopes used for obtaining authorization to use the Click API, see [Required scopes](https://developers.docusign.com/docs/click-api/click101/auth/#required-scopes) + +For a list of code examples that use the Click API, see the [How-to guides overview](https://developers.docusign.com/docs/click-api/how-to/) on the Docusign Developer Center. + ## Monitor API -1. **Get monitor data.** -[Source.](./app/monitor/examples/eg001_get_monitoring_data/controller.py) -This example demonstrates how to get and display all of your organization’s monitoring data. -## Included OAuth grant types: +**Note:** To use the Monitor API, you must also [enable Docusign Monitor for your organization](https://developers.docusign.com/docs/monitor-api/how-to/enable-monitor/). -* Authentication with Docusign via [Authorization Code Grant flow](https://developers.docusign.com/esign-rest-api/guides/authentication/oauth2-code-grant) . -When the token expires, the user is asked to re-authenticate. -The **refresh token** is not used in this example. +For information about the scopes used for obtaining authorization to use the Monitor API, see the [scopes section](https://developers.docusign.com/docs/monitor-api/monitor101/auth/). -* Authentication with DocuSign via the [JSON Web Token (JWT) Grant](https://developers.docusign.com/esign-rest-api/guides/authentication/oauth2-jsonwebtoken). -When the token expires, it updates automatically. +For a list of code examples that use the Monitor API, see the [How-to guides overview](https://developers.docusign.com/docs/monitor-api/how-to/) on the Docusign Developer Center. + + +## Admin API + +**Note:** To use the Admin API, you must [create an organization](https://support.docusign.com/en/guides/org-admin-guide-create-org) in your Docusign developer account. Also, to run the Docusign CLM code example, [CLM must be enabled for your organization](https://support.docusign.com/en/articles/DocuSign-and-SpringCM). + +For information about the scopes used for obtaining authorization to use the Admin API, see the [scopes section](https://developers.docusign.com/docs/admin-api/admin101/auth/). +For a list of code examples that use the Admin API, see the [How-to guides overview](https://developers.docusign.com/docs/admin-api/how-to/) on the Docusign Developer Center. + + +## Web Forms API + +The Web Forms API is available in all developer accounts, but only in certain production account plans. Contact [Docusign Support](https://support.docusign.com/) or your account manager to find out whether the Web Forms API is available for your production account plan. + +For more information about the scopes used for obtaining authorization to use the Rooms API, see [Required scopes](https://developers.docusign.com/docs/web-forms-api/plan-integration/authentication/). + +For a list of code examples that use the Web Forms API, see the [How-to guides overview](https://developers.docusign.com/docs/web-forms-api/how-to/) on the Docusign Developer Center. ## Installation ### Prerequisites -**Note: If you downloaded this code using Quickstart from the DocuSign Developer Center, skip steps 1 and 2 below as they're automatically performed for you.** +**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the Docusign Developer Center, skip items 1 and 2 as they were automatically performed for you. -1. A DocuSign Developer account (email and password) on [demo.docusign.net](https://demo.docusign.net). - Create a [free account](https://go.docusign.com/sandbox/productshot/?elqCampaignId=16535). -1. A DocuSign Integration Key (a client ID). To use Authorization code grant, you will need the **Integration Key** itself, and its **secret**. To use JSON Web token, you will need the **Integration Key** itself, the **RSA Secret Key** and an API user ID for the user you are impersonating. +1. A free [Docusign developer account](https://go.docusign.com/o/sandbox/); create one if you don't already have one. +1. A Docusign app and integration key that is configured to use either [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode/) or [JWT Grant](https://developers.docusign.com/platform/auth/jwt/) authentication. - If you use this example on your own workstation, - the Integration key must include a **Redirect URI** of `http://localhost:5000/ds/callback` + This [video](https://www.youtube.com/watch?v=eiRI4fe5HgM) demonstrates how to obtain an integration key. + + To use [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode/), you will need an integration key and a secret key. See [Installation steps](#installation-steps) for details. + + To use [JWT Grant](https://developers.docusign.com/platform/auth/jwt/), you will need an integration key, an RSA key pair, and the User ID GUID of the impersonated user. See [Installation steps for JWT Grant authentication](#installation-steps-for-jwt-grant-authentication) for details. - If you will not be running the example on your own workstation, - use the appropriate DNS name and port instead of `localhost` + For both authentication flows: + + If you use this launcher on your own workstation, the integration key must include a redirect URI of http://localhost:3000/ds/callback + + If you host this launcher on a remote web server, set your redirect URI as + + {base_url}/ds/callback - This [**video**](https://www.youtube.com/watch?v=eiRI4fe5HgM) - demonstrates how to create an Integration Key (client id) for a - user application like this example. Note that the redirect url for your - Integration Key will be `http://localhost:5000/ds/callback` if you - use the default Python settings. + where {base_url} is the URL for the web app. 1. Python 3. -1. A name and email for a signer, and a name and email for a cc recipient. +1. If you are working on a Windows with a Python version after 3.10 you will also need to install [Microsoft C++ Build Tools](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022). After installing that run `pip install --upgrade setuptools`. ### Installation steps -**Note: If you downloaded this code using Quickstart from the DocuSign Developer Center, skip steps 4 and 5 below as they're automatically performed for you.** -1. Extract the Quickstart ZIP file or download or clone the **code-examples-python** repo. -1. Switch to the folder: **cd ** or **cd code-examples-python** -1. **pip3 install -r requirements.txt** (or pipenv can be used) -1. Make a copy of the **app/ds_config_sample.py** and name it **ds_config.py** -1. Update this new file **app/ds_config.py** - with your Integration Key and other settings. +**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the Docusign Developer Center, skip step 4 as it was automatically performed for you. - **Note:** Protect your Integration Key and secret--you - should ensure that ds_config.py file will not be stored in your source code - repository. +1. Extract the Quickstart ZIP file or download or clone the code-examples-python repository. +1. In your command-line environment, switch to the folder: + `cd ` or `cd code-examples-python` +1. To install dependencies, run: `pip3 install -r requirements.txt` (or pipenv can be used) +1. To configure the launcher for [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode/) authentication, create a copy of the file app/ds_config_sample.py and save the copy as app/ds_config.py. + 1. Add your integration key. On the [Apps and Keys](https://admindemo.docusign.com/authenticate?goTo=apiIntegratorKey) page, under **Apps and Integration Keys**, choose the app to use, then select **Actions > Edit**. Under **General Info**, copy the **Integration Key** GUID and save it in ds_config.py as your `ds_client_id`. + 1. Generate a secret key, if you don’t already have one. Under **Authentication**, select **+ ADD SECRET KEY**. Copy the secret key and save it in ds_config.py as your `ds_client_secret`. + 1. Add the launcher’s redirect URI. Under **Additional settings**, select **+ ADD URI**, and set a redirect URI of http://localhost:3000/ds/callback. Select **SAVE**. + 1. Set a name and email address for the signer. In ds_config.py, save an email address as `signer_email` and a name as `signer_name`. +**Note:** Protect your personal information. Please make sure that ds_config.py will not be stored in your source code repository. +1. Run the launcher:`python run.py` + **Note:** You will need to alias the python command to run Python 3 or use `python3 run.py` +1. Open a browser to http://localhost:3000 -1. **python run.py** +### Installation steps with docker - **Note:** You will need to alias the python command to run Python 3 or use **python3 run.py** - -1. Open a browser to **http://localhost:5000** +**Note**: Running the launcher with docker will use Python 3.11 + +1. Open the Docker application +1. `docker image build -t docusign .` +1. `docker run --name docusign_python -p 3000:3000 -d docusign` +1. Open a browser to http://localhost:3000 + +### Installation steps for JWT Grant authentication + +**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the Docusign Developer Center, skip step 4 as it was automatically performed for you. +Also, in order to select JSON Web Token authentication in the launcher, in app/ds_config.py, change the `quickstart` setting to `"false"`. + +1. Extract the Quickstart ZIP file or download or clone the code-examples-python repository. +1. In your command-line environment, switch to the folder: `cd ` or `cd code-examples-python` +1. To install dependencies, run: `pip3 install -r requirements.txt` (or pipenv can be used) +1. To configure the launcher for [JWT Grant](https://developers.docusign.com/platform/auth/jwt/) authentication, create a copy of the file app/ds_config_sample.py and save the copy as app/ds_config.py. + 1. Add your User ID. On the [Apps and Keys](https://admindemo.docusign.com/authenticate?goTo=apiIntegratorKey) page, under **My Account Information**, copy the **User ID** GUID and save it in ds_config.py as your `ds_impersonated_user_id`. + 1. Add your integration key. On the [Apps and Keys](https://admindemo.docusign.com/authenticate?goTo=apiIntegratorKey) page, under **Apps and Integration Keys**, choose the app to use, then select **Actions > Edit**. Under **General Info**, copy the **Integration Key** GUID and save it in ds_config.py as your `ds_client_id`. + 1. Generate an RSA key pair, if you don’t already have one. Under **Authentication**, select **+ GENERATE RSA**. Copy the private key, and save it in a new file named app/private.key. + 1. Add the launcher’s redirect URI. Under **Additional settings**, select **+ ADD URI**, and set a redirect URI of http://localhost:3000/ds/callback. Select **SAVE**. + 1. Set a name and email address for the signer. In ds_config.py, save an email address as `signer_email` and a name as `signer_name`. +**Note:** Protect your personal information. Please make sure that your ds_config.py and private.key files will not be stored in your source code repository. +1. Run the launcher:`python run.py` + **Note:** You will need to alias the python command to run Python 3 or use `python3 run.py` +1. Open a browser to http://localhost:3000 +1. On the black navigation bar, select **Login**. +1. From the picklist, select **JSON Web Token** > **Authenticate with Docusign**. +1. When prompted, log in to your Docusign developer account. If this is your first time using the app, select **ACCEPT** at the consent window. +1. Select your desired code example. + + +## JWT grant remote signing and Authorization Code Grant embedded signing projects + +See [Docusign Quickstart overview](https://developers.docusign.com/docs/esign-rest-api/quickstart/overview/) on the Docusign Developer Center for more information on how to run the JWT grant remote signing project and the Authorization Code Grant embedded signing project. + +### Authorization Code Grant embedded signing example: +Run in Git Bash: +``` +$ cd +$ pip install -r requirements.txt +$ python3 -m app.quick_acg.run +``` -### Configuring JWT +Open a browser to http://localhost:3000 -1. Create a developer account on developers.docusign.com if you don't already have one. -2. Create a new API key in the Admin panel: https://admindemo.docusign.com/api-integrator-key, take note of the public key. -3. Set a redirect URI of `http://localhost:5000/ds/callback` as mentioned in the installation steps above for the API key you make in step 2. -4. Generate an RSA keypair in the administrator console on the DocuSign developer account and copy the private key to a secure location. -5. Create a new file in your repo source folder named **private.key**, and paste in that copied RSA private key, then save it. -6. Update the file **app/ds_config.py** and include the newly created API key from step 2 as well as your account user id GUID which is also found on the Admin panel: `https://admindemo.docusign.com/api-integrator-key`. +### JWT grant remote signing example: +Run in Windows Command Prompt (CMD): +``` +$ cd +$ python3 jwt_console.py +``` -From there you should be able to run the launcher using **python run.py** then selecting **JSON Web Token** when authenticaing your account. +### Installation steps for JWT grant remote signing example -#### Payments code example -To use the payments example, create a -test payments gateway for your developer account. +Follow the instructions below if you downloaded the JWT grant remote signing example. -See the -[PAYMENTS_INSTALLATION.md](https://github.com/docusign/code-examples-python/blob/master/PAYMENTS_INSTALLATION.md) -file for instructions. +1. Extract the Quickstart ZIP file or download or clone the code-examples-python repository. +1. Run the JWT console project: `python jwt_console.py`. If you do not already have the docusign-esign package, it will be + installed for you when you start the project. + **Note:** You will need to alias the python command to run Python 3 or use `python3 run.py` +1. Follow the prompts from the command line to grant consent in your browser. +1. Return to the command line and follow the prompts to enter the names and email addresses of your recipients. +1. Check your email to sign the example document. -Then add the payment gateway account id to the **app/ds_config.py** file. +## Payments code example +To use the payments code example, create a test payment gateway on the [Payments](https://admindemo.docusign.com/authenticate?goTo=payments) page in your developer account. See [Configure a payment gateway](./PAYMENTS_INSTALLATION.md) for details. -## License and additional information +Once you've created a payment gateway, save the **Gateway Account ID** GUID to ds_config.py. -### Implicit Grant -The examples in this repository can also be used with the -Implicit Grant OAuth flow. -See the [Authentication guide](https://developers.docusign.com/esign-rest-api/guides/authentication) -for information on choosing the right authentication flow for your application. +## License and additional information -### License -This repository uses the MIT License. See the LICENSE file for more information. +### License +This repository uses the MIT License. See [LICENSE](./LICENSE) for details. ### Pull Requests Pull requests are welcomed. Pull requests will only be considered if their content diff --git a/app/__init__.py b/app/__init__.py index fbb195a6..2f38180a 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,106 +1,135 @@ -import os - -from flask import Flask -from flask_wtf.csrf import CSRFProtect - -from .ds_config import DS_CONFIG -from .eSignature import examples -from .docusign.views import ds -from .ds_config import EXAMPLES_API_TYPE -from .rooms import examples as rooms_examples -from .click import examples as click_examples -from .monitor import examples as monitor_examples -from .views import core - -session_path = "/tmp/python_recipe_sessions" - -if EXAMPLES_API_TYPE["Rooms"]: - app = Flask(__name__, template_folder="rooms/templates") -elif EXAMPLES_API_TYPE["Click"]: - app = Flask(__name__, template_folder="click/templates") -elif EXAMPLES_API_TYPE["Monitor"]: - app = Flask(__name__, template_folder="monitor/templates") -else: - app = Flask(__name__) -app.config.from_pyfile("config.py") - -# See https://flask-wtf.readthedocs.io/en/stable/csrf.html -csrf = CSRFProtect(app) - -# Set whether this is a quickstart in config -#app.config["quickstart"] = DS_CONFIG["quickstart"] - -# Set whether user has logged in -#app.config["isLoggedIn"] = False - -# Register home page -app.register_blueprint(core) - -# Register OAuth -app.register_blueprint(ds) -# Register examples -if EXAMPLES_API_TYPE["Rooms"]: - app.register_blueprint(rooms_examples.eg001Rooms) - app.register_blueprint(rooms_examples.eg002) - app.register_blueprint(rooms_examples.eg003) - app.register_blueprint(rooms_examples.eg004) - app.register_blueprint(rooms_examples.eg005) - app.register_blueprint(rooms_examples.eg006) - app.register_blueprint(rooms_examples.eg007) - app.register_blueprint(rooms_examples.eg008) - app.register_blueprint(rooms_examples.eg009) - -elif EXAMPLES_API_TYPE["Monitor"]: - app.register_blueprint(monitor_examples.eg001) - -elif EXAMPLES_API_TYPE["Click"]: - app.register_blueprint(click_examples.eg001) - app.register_blueprint(click_examples.eg002) - app.register_blueprint(click_examples.eg003) - app.register_blueprint(click_examples.eg004) - app.register_blueprint(click_examples.eg005) -else: - app.register_blueprint(examples.eg001) - app.register_blueprint(examples.eg002) - app.register_blueprint(examples.eg003) - app.register_blueprint(examples.eg004) - app.register_blueprint(examples.eg005) - app.register_blueprint(examples.eg006) - app.register_blueprint(examples.eg007) - app.register_blueprint(examples.eg008) - app.register_blueprint(examples.eg009) - app.register_blueprint(examples.eg010) - app.register_blueprint(examples.eg011) - app.register_blueprint(examples.eg012) - app.register_blueprint(examples.eg013) - app.register_blueprint(examples.eg014) - app.register_blueprint(examples.eg015) - app.register_blueprint(examples.eg016) - app.register_blueprint(examples.eg017) - app.register_blueprint(examples.eg018) - app.register_blueprint(examples.eg019) - app.register_blueprint(examples.eg020) - app.register_blueprint(examples.eg021) - app.register_blueprint(examples.eg022) - app.register_blueprint(examples.eg023) - app.register_blueprint(examples.eg024) - app.register_blueprint(examples.eg025) - app.register_blueprint(examples.eg026) - app.register_blueprint(examples.eg027) - app.register_blueprint(examples.eg028) - app.register_blueprint(examples.eg029) - app.register_blueprint(examples.eg030) - app.register_blueprint(examples.eg031) - app.register_blueprint(examples.eg032) - app.register_blueprint(examples.eg033) - app.register_blueprint(examples.eg034) - app.register_blueprint(examples.eg035) - -if "DYNO" in os.environ: # On Heroku? - import logging - - stream_handler = logging.StreamHandler() - app.logger.addHandler(stream_handler) - app.logger.setLevel(logging.INFO) - app.logger.info("Recipe example startup") - app.config.update(dict(PREFERRED_URL_SCHEME="https")) +import os + +from flask import Flask, session, current_app +from flask_wtf.csrf import CSRFProtect + +from .ds_config import DS_CONFIG +from .eSignature import views as esignature_views +from .docusign.views import ds +from .api_type import EXAMPLES_API_TYPE +from .rooms import views as rooms_views +from .click import views as click_views +from .monitor import views as monitor_views +from .admin import views as admin_views +from .connect import views as connect_views +from .webforms import views as webforms_views +from .notary import views as notary_views +from .connected_fields import views as connected_fields_views +from .views import core + +session_path = "/tmp/python_recipe_sessions" +app = Flask(__name__) + +app.config.from_pyfile("config.py") + +# See https://flask-wtf.readthedocs.io/en/stable/csrf.html +csrf = CSRFProtect(app) + +# Set whether this is a quickstart in config +#app.config["quickstart"] = DS_CONFIG["quickstart"] + +# Set whether user has logged in +#app.config["isLoggedIn"] = False + +# Register home page +app.register_blueprint(core) + +# Register OAuth +app.register_blueprint(ds) +# Register examples + +app.register_blueprint(rooms_views.reg001) +app.register_blueprint(rooms_views.reg002) +app.register_blueprint(rooms_views.reg003) +app.register_blueprint(rooms_views.reg004) +app.register_blueprint(rooms_views.reg005) +app.register_blueprint(rooms_views.reg006) +app.register_blueprint(rooms_views.reg007) +app.register_blueprint(rooms_views.reg008) +app.register_blueprint(rooms_views.reg009) + +app.register_blueprint(monitor_views.meg001) + +app.register_blueprint(admin_views.aeg001) +app.register_blueprint(admin_views.aeg002) +app.register_blueprint(admin_views.aeg003) +app.register_blueprint(admin_views.aeg004) +app.register_blueprint(admin_views.aeg005) +app.register_blueprint(admin_views.aeg006) +app.register_blueprint(admin_views.aeg007) +app.register_blueprint(admin_views.aeg008) +app.register_blueprint(admin_views.aeg009) +app.register_blueprint(admin_views.aeg010) +app.register_blueprint(admin_views.aeg011) +app.register_blueprint(admin_views.aeg012) +app.register_blueprint(admin_views.aeg013) + +app.register_blueprint(click_views.ceg001) +app.register_blueprint(click_views.ceg002) +app.register_blueprint(click_views.ceg003) +app.register_blueprint(click_views.ceg004) +app.register_blueprint(click_views.ceg005) +app.register_blueprint(click_views.ceg006) + +app.register_blueprint(esignature_views.eg001) +app.register_blueprint(esignature_views.eg002) +app.register_blueprint(esignature_views.eg003) +app.register_blueprint(esignature_views.eg004) +app.register_blueprint(esignature_views.eg005) +app.register_blueprint(esignature_views.eg006) +app.register_blueprint(esignature_views.eg007) +app.register_blueprint(esignature_views.eg008) +app.register_blueprint(esignature_views.eg009) +app.register_blueprint(esignature_views.eg010) +app.register_blueprint(esignature_views.eg011) +app.register_blueprint(esignature_views.eg012) +app.register_blueprint(esignature_views.eg013) +app.register_blueprint(esignature_views.eg014) +app.register_blueprint(esignature_views.eg015) +app.register_blueprint(esignature_views.eg016) +app.register_blueprint(esignature_views.eg017) +app.register_blueprint(esignature_views.eg018) +app.register_blueprint(esignature_views.eg019) +app.register_blueprint(esignature_views.eg020) +app.register_blueprint(esignature_views.eg022) +app.register_blueprint(esignature_views.eg023) +app.register_blueprint(esignature_views.eg024) +app.register_blueprint(esignature_views.eg025) +app.register_blueprint(esignature_views.eg026) +app.register_blueprint(esignature_views.eg027) +app.register_blueprint(esignature_views.eg028) +app.register_blueprint(esignature_views.eg029) +app.register_blueprint(esignature_views.eg030) +app.register_blueprint(esignature_views.eg031) +app.register_blueprint(esignature_views.eg032) +app.register_blueprint(esignature_views.eg033) +app.register_blueprint(esignature_views.eg034) +app.register_blueprint(esignature_views.eg035) +app.register_blueprint(esignature_views.eg036) +app.register_blueprint(esignature_views.eg037) +app.register_blueprint(esignature_views.eg038) +app.register_blueprint(esignature_views.eg039) +app.register_blueprint(esignature_views.eg040) +app.register_blueprint(esignature_views.eg041) +app.register_blueprint(esignature_views.eg042) +app.register_blueprint(esignature_views.eg043) +app.register_blueprint(esignature_views.eg044) +app.register_blueprint(esignature_views.eg045) + +app.register_blueprint(connect_views.cneg001) + +app.register_blueprint(webforms_views.weg001) +app.register_blueprint(webforms_views.weg002) + +app.register_blueprint(notary_views.neg004) + +app.register_blueprint(connected_fields_views.feg001) + +if "DYNO" in os.environ: # On Heroku? + import logging + + stream_handler = logging.StreamHandler() + app.logger.addHandler(stream_handler) + app.logger.setLevel(logging.INFO) + app.logger.info("Recipe example startup") + app.config.update(dict(PREFERRED_URL_SCHEME="https")) diff --git a/app/admin/__init__.py b/app/admin/__init__.py new file mode 100644 index 00000000..2b335eb4 --- /dev/null +++ b/app/admin/__init__.py @@ -0,0 +1 @@ +from .utils import create_admin_api_client diff --git a/app/admin/examples/csv/uploaded_file.csv b/app/admin/examples/csv/uploaded_file.csv new file mode 100644 index 00000000..ea2c5ad1 --- /dev/null +++ b/app/admin/examples/csv/uploaded_file.csv @@ -0,0 +1,3 @@ +AccountID,UserName,UserEmail,PermissionSet +xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,John Dow1,exampleuser1@example.com,DS Viewer +xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,John Dow2,exampleuser2@example.com,DS Viewer diff --git a/app/admin/examples/eg001_create_a_new_user.py b/app/admin/examples/eg001_create_a_new_user.py new file mode 100644 index 00000000..8f2cf1c4 --- /dev/null +++ b/app/admin/examples/eg001_create_a_new_user.py @@ -0,0 +1,135 @@ +from docusign_admin import UsersApi, NewUserRequest, NewUserRequestAccountProperties, PermissionProfileRequest, GroupRequest +from docusign_esign import AccountsApi, ApiClient, GroupsApi +from flask import session + +from app.admin.utils import create_admin_api_client, get_organization_id +from app.consts import pattern +from app.ds_config import DS_CONFIG + + +class Eg001CreateNewUserController: + + @staticmethod + def get_args(request): + """ + Get request and session arguments + """ + + organization_id = get_organization_id() + return { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + "user_name": request.form.get("user_name"), + "first_name": request.form.get("first_name"), + "last_name": request.form.get("last_name"), + "user_email": request.form.get("user_email"), + "permission_profile": request.form.get("profile_id"), + "group": request.form.get("group_id"), + "organization_id": organization_id + } + + @staticmethod + def get_permission_profiles(args): + """Get permission profiles""" + + access_token = args["access_token"] + account_id = args["account_id"] + + api_client = ApiClient(host=session["ds_base_path"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + + #ds-snippet-start:Admin1Step3 + accounts_api = AccountsApi(api_client=api_client) + profiles = accounts_api.list_permissions(account_id=account_id) + profiles_list = profiles.to_dict()["permission_profiles"] + #ds-snippet-end:Admin1Step3 + return profiles_list + + @staticmethod + def get_groups(args): + """Get ds groups""" + + access_token = args["access_token"] + account_id = args["account_id"] + + # Create an API client with headers + api_client = ApiClient(host=session["ds_base_path"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + + #ds-snippet-start:Admin1Step4 + groups_api = GroupsApi(api_client) + groups = groups_api.list_groups(account_id=account_id) + groups_dict = groups.to_dict() + groups_list = groups_dict["groups"] + #ds-snippet-end:Admin1Step4 + return groups_list + + @staticmethod + def worker(self, args): + """ + 1. Create the API client object + 2. Create the user API request object + 3. Create a request body for the create_user method + 4. Creates a user using a method from the user API + """ + + # Get group information + groups = self.get_groups(args) + for group in groups: + if group["group_id"] == args["group"]: + group_name = group["group_name"] + group_type = group["group_type"] + + # Get permission profile information + permission_profiles = self.get_permission_profiles(args) + for profile in permission_profiles: + if profile["permission_profile_id"] == args["permission_profile"]: + profile_name = profile["permission_profile_name"] + + # Create the API client object + #ds-snippet-start:Admin1Step2 + api_client = create_admin_api_client( + access_token=session["ds_access_token"] + ) + # Create the user API request object + user_api = UsersApi(api_client=api_client) + #ds-snippet-end:Admin1Step2 + + # Create a request body for the create_user method + #ds-snippet-start:Admin1Step5 + request_body = { + "user_name": args["user_name"], + "first_name": args['first_name'], + "last_name": args['last_name'], + "email": args['user_email'], + "auto_activate_memberships": True, + "accounts": [ + { + "id": session["ds_account_id"], + "permission_profile": { + "id": args['permission_profile'], + }, + "groups": [ + { + "id": args["group"], + } + ] + } + ] + } + #ds-snippet-end:Admin1Step5 + + # Creates a user using a method from the user API + #ds-snippet-start:Admin1Step6 + response = user_api.create_user( + args["organization_id"], + request_body + ) + #ds-snippet-end:Admin1Step6 + return response diff --git a/app/admin/examples/eg002_create_active_clm_esign_user.py b/app/admin/examples/eg002_create_active_clm_esign_user.py new file mode 100644 index 00000000..c984b46d --- /dev/null +++ b/app/admin/examples/eg002_create_active_clm_esign_user.py @@ -0,0 +1,114 @@ +from docusign_admin import ApiClient, ProductPermissionProfilesApi, DSGroupsApi, UsersApi, NewMultiProductUserAddRequest, ProductPermissionProfileRequest, DSGroupRequest +from flask import session, json, request + +from ...ds_config import DS_CONFIG +from app.admin.utils import get_organization_id + +class Eg002CreateActiveClmEsignUserController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + organization_id = get_organization_id() + return { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + "organization_id": organization_id, # Represents your {ORGANIZATION_ID} + "clm_permission_profile_name": request.form.get("clm_permission_profile"), + "esign_permission_profile_name": request.form.get("esign_permission_profile"), + "user_name": request.form.get("user_name"), + "first_name": request.form.get("first_name"), + "last_name": request.form.get("last_name"), + "email": request.form.get("email"), + "group_id": request.form.get("ds_group"), + } + + @staticmethod + def get_permission_profiles(args): + """Get permission profiles""" + + access_token = args["access_token"] + account_id = args["account_id"] + org_id = args["organization_id"] + + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + #ds-snippet-start:Admin2Step3 + product_permission_profiles_api = ProductPermissionProfilesApi(api_client=api_client) + profiles = product_permission_profiles_api.get_product_permission_profiles(organization_id=org_id, account_id=session["ds_account_id"]) + profiles_list = profiles.to_dict()["product_permission_profiles"] + #ds-snippet-end:Admin2Step3 + return profiles_list + + @staticmethod + def get_groups(args): + """Get ds groups""" + + access_token = args["access_token"] + account_id = args["account_id"] + org_id = args["organization_id"] + + # Create an API client with headers + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + + #ds-snippet-start:Admin2Step4 + ds_groups_api = DSGroupsApi(api_client) + ds_groups = ds_groups_api.get_ds_groups(organization_id=org_id, account_id=session["ds_account_id"]) + #ds-snippet-end:Admin2Step4 + return ds_groups + + @staticmethod + def worker(self, args): + """ + 1. Create an API client with headers + 2. Get your monitor data via SDK + """ + + access_token = args["access_token"] + account_id = args["account_id"] + org_id = args["organization_id"] + clm_permission_profile_name = args["clm_permission_profile_name"] + esign_permission_profile_name = args["esign_permission_profile_name"] + + # Create an API client with headers + #ds-snippet-start:Admin2Step2 + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + #ds-snippet-end:Admin2Step2 + + profiles_list = self.get_permission_profiles(args) + + for profile in profiles_list: + if profile["product_name"] == "CLM": + clm_product_id = profile["product_id"] + for permission_profile in profile["permission_profiles"]: + if permission_profile["permission_profile_name"] == clm_permission_profile_name: + clm_permission_profile_id = permission_profile["permission_profile_id"] + else: + esign_product_id = profile["product_id"] + for permission_profile in profile["permission_profiles"]: + if permission_profile["permission_profile_name"] == esign_permission_profile_name: + esign_permission_profile_id = permission_profile["permission_profile_id"] + + #ds-snippet-start:Admin2Step5 + clm_product_permission_profile = ProductPermissionProfileRequest(product_id=clm_product_id, permission_profile_id=clm_permission_profile_id) + esign_product_permission_profile = ProductPermissionProfileRequest(product_id=esign_product_id, permission_profile_id=esign_permission_profile_id) + ds_group_request = DSGroupRequest(ds_group_id=args["group_id"]) + new_user = NewMultiProductUserAddRequest(product_permission_profiles=[esign_product_permission_profile, clm_product_permission_profile], ds_groups=[ds_group_request], user_name=args["user_name"], first_name=args["first_name"], last_name=args["last_name"], email=args["email"], auto_activate_memberships=True) + #ds-snippet-end:Admin2Step5 + + #ds-snippet-start:Admin2Step6 + users_api = UsersApi(api_client) + response = users_api.add_or_update_user(organization_id=org_id, account_id=session["ds_account_id"], request=new_user) + #ds-snippet-end:Admin2Step6 + + return response.to_dict() diff --git a/app/admin/examples/eg003_bulk_export_user_data.py b/app/admin/examples/eg003_bulk_export_user_data.py new file mode 100644 index 00000000..713c1d14 --- /dev/null +++ b/app/admin/examples/eg003_bulk_export_user_data.py @@ -0,0 +1,99 @@ +from docusign_admin import ApiClient +from docusign_admin.apis import BulkExportsApi +from flask import session + +from app.admin.utils import create_admin_api_client, get_organization_id +from app.ds_config import DS_CONFIG + + +class Eg003BulkExportUserDataController: + + @classmethod + def worker(cls): + """ + 1. Create the export API object + 2. Create a user list export request + 3. Save user_list_export_id in a client session + 4. Returns a list of pending and completed export requests + """ + + organization_id = get_organization_id() + + api_client = create_admin_api_client( + access_token=session["ds_access_token"] + ) + + # Create the export API object + export_api = BulkExportsApi(api_client=api_client) + + # Create a user list export request + #ds-snippet-start:Admin3Step3 + response = export_api.create_user_list_export( + organization_id, + { + "type": "organization_memberships_export" + } + ) + #ds-snippet-end:Admin3Step3 + + # Save user_list_export_id in a client session + session['user_list_export_id'] = response.id + + # Returns a list of pending and completed export requests + return response + + @classmethod + def get_csv_user_list(cls): + """ + Getting the csv file of the current list of users: + 1. Create the export API object + 2. Getting the user list export response + 3. Trying to get the user list export id + 4. Create the API client object + 5. Add headers to the API client object and the desired URL + 6. Getting a response containing a csv file + 7. Returns the csv file + """ + + organization_id = get_organization_id() + + #ds-snippet-start:Admin3Step2 + api_client = create_admin_api_client( + access_token=session["ds_access_token"] + ) + + # Create the export API object + export_api = BulkExportsApi(api_client=api_client) + #ds-snippet-end:Admin3Step2 + + # Getting the user list export response + #ds-snippet-start:Admin3Step4 + response = export_api.get_user_list_export( + organization_id, + session['user_list_export_id'] + ) + #ds-snippet-end:Admin3Step4 + + # Trying to get the user list export id + try: + obj_id = response.results[0].id + except TypeError: + return None + + # Create the API client object + #ds-snippet-start:Admin3Step5 + api_client = ApiClient() + + # Add headers to the API client object and the desired URL + headers = {"Authorization": "Bearer " + session["ds_access_token"]} + url = ( + "https://demo.docusign.net/restapi/v2/organization_exports/" + f"{organization_id}/user_list/{obj_id}" + ) + + # Getting a response containing a csv file + response = api_client.request("GET", url, headers=headers) + #ds-snippet-end:Admin3Step5 + + # Returns the csv file + return response.data.decode("UTF8") diff --git a/app/admin/examples/eg004_add_users_via_bulk_import.py b/app/admin/examples/eg004_add_users_via_bulk_import.py new file mode 100644 index 00000000..a1240696 --- /dev/null +++ b/app/admin/examples/eg004_add_users_via_bulk_import.py @@ -0,0 +1,101 @@ +from os import path +from docusign_admin.apis import BulkImportsApi +from flask import session, Response +import time + +from app.admin.utils import create_admin_api_client, get_organization_id +from app.ds_config import DS_CONFIG + + +class Eg004AddUsersViaBulkImportController: + @staticmethod + def get_example_csv(): + """ + Creates an example of a CSV file, such as that needs to be sent to the Docusign server + """ + + # Returns an example of a CSV file + return ( + "AccountID,UserName,UserEmail,PermissionSet\n" + f"{session['ds_account_id']},Example User1,exampleuser1@example.com,DS Admin\n" + f"{session['ds_account_id']},Example User2,exampleuser2@example.com,DS Admin\n" + ) + + @staticmethod + def worker(self, request): + """ + Create a user list import request and + returns a list of pending and completed import requests: + 1. Create the import API object + 2. Getting a CSV file from a form and converting it to a string + 3. Creating an import API object + 4. Setting headers for creating bulk import request + 5. Returns the response from the create_bulk_import_add_users_request method + """ + + # Get organization ID + organization_id = get_organization_id() + + # Create the export API object + #ds-snippet-start:Admin4Step2 + api_client = create_admin_api_client( + access_token=session["ds_access_token"] + ) + #ds-snippet-end:Admin4Step2 + + # Getting a CSV file from a form and saving it + uploaded_file = request.files['csv_file'] + csv_folder_path = path.abspath(path.join(path.dirname(path.realpath(__file__)), "csv")) + csv_file_path = path.join(csv_folder_path, "uploaded_file.csv") + + if uploaded_file and not uploaded_file.filename == '': + uploaded_file.save(csv_file_path) + else: + content = Eg004AddUsersViaBulkImportController.get_example_csv() + + modified_content = content.replace("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", session["ds_account_id"]) + + with open(csv_file_path, "w") as file: + file.write(modified_content) + + # Creating an import API object + #ds-snippet-start:Admin4Step3 + import_api = BulkImportsApi(api_client=api_client) + + # Setting headers for creating bulk import request + header_name, header_value = "Content-Disposition", "filename=myfile.csv" + api_client.set_default_header(header_name, header_value) + + # Returns the response from the create_bulk_import_add_users_request method + response = import_api.create_bulk_import_add_users_request( + organization_id, + csv_file_path + ) + #ds-snippet-end:Admin4Step3 + + # Save user list import id in a client session + session['import_data_id'] = response.id + + return response + + @staticmethod + def check_status(): + """Check request status""" + + organization_id = get_organization_id() + + api_client = create_admin_api_client( + access_token=session["ds_access_token"] + ) + + # Creating an import API object + import_api = BulkImportsApi(api_client=api_client) + + #ds-snippet-start:Admin4Step4 + import_results = import_api.get_bulk_user_import_request(organization_id, session['import_data_id']) + #ds-snippet-end:Admin4Step4 + + if import_results.status == "completed": + return import_results + else: + return None diff --git a/app/admin/examples/eg005_audit_users.py b/app/admin/examples/eg005_audit_users.py new file mode 100644 index 00000000..002edcd7 --- /dev/null +++ b/app/admin/examples/eg005_audit_users.py @@ -0,0 +1,68 @@ +from docusign_admin import ApiClient, UsersApi +from flask import session, json + +from ...ds_config import DS_CONFIG +from app.admin.utils import get_organization_id +import datetime + +class Eg005AuditUsersController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + organization_id = get_organization_id() + return { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + "organization_id": organization_id, # Represents your {ORGANIZATION_ID} + } + + @staticmethod + def worker(args): + """ + 1. Create an API client with headers + 2. Get your monitor data via SDK + """ + + access_token = args["access_token"] + account_id = args["account_id"] + org_id = args["organization_id"] + + # Create an API client with headers + #ds-snippet-start:Admin5Step2 + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + #ds-snippet-end:Admin5Step2 + + #ds-snippet-start:Admin5Step3 + today = datetime.datetime.now() + ten_days_ago = today - (datetime.timedelta(days = 10)) + last_modified_since = ten_days_ago.strftime('%Y-%m-%d') + + users_api = UsersApi(api_client=api_client) + users = users_api.get_users( + organization_id=org_id, + account_id=account_id, + last_modified_since=last_modified_since) + #ds-snippet-end:Admin5Step3 + + #ds-snippet-start:Admin5Step4 + modified_users = users.users + emails = [] + for user in modified_users: + dict_user = user.to_dict() + emails.append(dict_user["email"]) + #ds-snippet-end:Admin5Step4 + + #ds-snippet-start:Admin5Step5 + profile_list = [] + for email in emails: + profile = users_api.get_user_profiles(organization_id=org_id, email=email) + profile_list.append(profile.to_dict()) + + results = {"Modified users": profile_list} + #ds-snippet-end:Admin5Step5 + + return results diff --git a/app/admin/examples/eg006_get_user_profile_by_email.py b/app/admin/examples/eg006_get_user_profile_by_email.py new file mode 100644 index 00000000..8a882992 --- /dev/null +++ b/app/admin/examples/eg006_get_user_profile_by_email.py @@ -0,0 +1,47 @@ +from docusign_admin import ApiClient, UsersApi +from flask import session, request + +from ...ds_config import DS_CONFIG +from app.admin.utils import get_organization_id + +class Eg006GetUserProfileByEmailController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + organization_id = get_organization_id() + return { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + "organization_id": organization_id, # Represents your {ORGANIZATION_ID}, + "email": request.form.get("email"), + } + + @staticmethod + def worker(args): + """ + 1. Create an API client with headers + 2. Get user profile data + """ + + access_token = args["access_token"] + org_id = args["organization_id"] + email = args["email"] + + # Create an API client with headers + #ds-snippet-start:Admin6Step2 + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + #ds-snippet-end:Admin6Step2 + + #ds-snippet-start:Admin6Step3 + users_api = UsersApi(api_client=api_client) + + results = users_api.get_user_ds_profiles_by_email( + organization_id=org_id, + email=email) + #ds-snippet-end:Admin6Step3 + + return results diff --git a/app/admin/examples/eg007_get_user_profile_by_user_id.py b/app/admin/examples/eg007_get_user_profile_by_user_id.py new file mode 100644 index 00000000..380bd069 --- /dev/null +++ b/app/admin/examples/eg007_get_user_profile_by_user_id.py @@ -0,0 +1,47 @@ +from docusign_admin import ApiClient, UsersApi +from flask import session, request + +from ...ds_config import DS_CONFIG +from app.admin.utils import get_organization_id + +class Eg007GetUserProfileByUserIdController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + organization_id = get_organization_id() + return { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + "organization_id": organization_id, # Represents your {ORGANIZATION_ID}, + "user_id": request.form.get("user_id"), + } + + @staticmethod + def worker(args): + """ + 1. Create an API client with headers + 2. Get user profile data + """ + + access_token = args["access_token"] + org_id = args["organization_id"] + user_id = args["user_id"] + + # Create an API client with headers + #ds-snippet-start:Admin7Step2 + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + #ds-snippet-end:Admin7Step2 + + #ds-snippet-start:Admin7Step3 + users_api = UsersApi(api_client=api_client) + + results = users_api.get_user_ds_profile( + organization_id=org_id, + user_id=user_id) + #ds-snippet-end:Admin7Step3 + + return results diff --git a/app/admin/examples/eg008_update_user_product_permission_profile.py b/app/admin/examples/eg008_update_user_product_permission_profile.py new file mode 100644 index 00000000..8a76a0ce --- /dev/null +++ b/app/admin/examples/eg008_update_user_product_permission_profile.py @@ -0,0 +1,86 @@ +from docusign_admin import ( + ApiClient, + ProductPermissionProfilesApi, + UserProductPermissionProfilesRequest, + ProductPermissionProfileRequest) +from flask import session, request + +from ...ds_config import DS_CONFIG +from app.admin.utils import get_organization_id + +class Eg008UpdateUserProductPermissionProfileController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + organization_id = get_organization_id() + + return { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + "organization_id": organization_id, # Represents your {ORGANIZATION_ID} + "product_id": request.form.get("product"), + "clm_email": session["clm_email"], + } + + @staticmethod + def get_permission_profiles(): + """Get permission profiles""" + + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {session['ds_access_token']}" + ) + + product_permission_profiles_api = ProductPermissionProfilesApi(api_client=api_client) + profiles = product_permission_profiles_api.get_product_permission_profiles( + organization_id=get_organization_id(), + account_id=session["ds_account_id"] + ) + profiles_list = profiles.to_dict()["product_permission_profiles"] + return profiles_list + + @staticmethod + def worker(self, args): + """ + 1. Create an API client with headers + 2. Get your monitor data via SDK + """ + + access_token = args["access_token"] + account_id = args["account_id"] + org_id = args["organization_id"] + clm_email = args["clm_email"] + permission_profile_id = args["permission_profile_id"] + product_id = args["product_id"] + + # Create an API client with headers + #ds-snippet-start:Admin8Step2 + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + #ds-snippet-end:Admin8Step2 + + #ds-snippet-start:Admin8Step3 + product_permission_profile = ProductPermissionProfileRequest( + permission_profile_id=permission_profile_id, + product_id=product_id + ) + user_product_permission_profile_request = UserProductPermissionProfilesRequest( + email=clm_email, + product_permission_profiles=[product_permission_profile] + ) + #ds-snippet-end:Admin8Step3 + + #ds-snippet-start:Admin8Step4 + product_permission_profiles_api = ProductPermissionProfilesApi(api_client=api_client) + response = product_permission_profiles_api.add_user_product_permission_profiles_by_email( + organization_id=org_id, + account_id=account_id, + user_product_permission_profiles_request=user_product_permission_profile_request + ) + #ds-snippet-end:Admin8Step4 + + return response.to_dict() diff --git a/app/admin/examples/eg009_delete_user_product_permission_profile.py b/app/admin/examples/eg009_delete_user_product_permission_profile.py new file mode 100644 index 00000000..05b6538f --- /dev/null +++ b/app/admin/examples/eg009_delete_user_product_permission_profile.py @@ -0,0 +1,81 @@ +from docusign_admin import ( + ApiClient, + ProductPermissionProfilesApi, + UserProductProfileDeleteRequest) +from flask import session, request + +from ...ds_config import DS_CONFIG +from app.admin.utils import get_organization_id + +class Eg009DeleteUserProductPermissionProfileController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + organization_id = get_organization_id() + return { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + "organization_id": organization_id, # Represents your {ORGANIZATION_ID} + "product_id": request.form.get("product_id"), + "clm_email": session["clm_email"], + } + + @staticmethod + def get_permission_profiles_by_email(): + """Get permission profiles""" + + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {session['ds_access_token']}" + ) + #ds-snippet-start:Admin9Step3 + product_permission_profiles_api = ProductPermissionProfilesApi(api_client=api_client) + profiles = product_permission_profiles_api.get_user_product_permission_profiles_by_email( + organization_id=get_organization_id(), + account_id=session["ds_account_id"], + email=session["clm_email"] + ) + profiles_list = profiles.to_dict()["product_permission_profiles"] + #ds-snippet-end:Admin9Step3 + return profiles_list + + @staticmethod + def worker(self, args): + """ + 1. Create an API client with headers + 2. Get your monitor data via SDK + """ + + access_token = args["access_token"] + account_id = args["account_id"] + org_id = args["organization_id"] + email = args["clm_email"] + product_id = args["product_id"] + + # Create an API client with headers + #ds-snippet-start:Admin9Step2 + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + #ds-snippet-end:Admin9Step2 + + #ds-snippet-start:Admin9Step4 + user_product_profile_delete_request = UserProductProfileDeleteRequest( + user_email=email, + product_ids=[product_id] + ) + #ds-snippet-end:Admin9Step4 + + #ds-snippet-start:Admin9Step5 + product_permission_profiles_api = ProductPermissionProfilesApi(api_client=api_client) + response = product_permission_profiles_api.remove_user_product_permission( + organization_id=org_id, + account_id=account_id, + user_product_permission_profiles_request=user_product_profile_delete_request + ) + #ds-snippet-end:Admin9Step5 + + return response.to_dict() diff --git a/app/admin/examples/eg010_delete_user_data_from_organization.py b/app/admin/examples/eg010_delete_user_data_from_organization.py new file mode 100644 index 00000000..7da5e8d2 --- /dev/null +++ b/app/admin/examples/eg010_delete_user_data_from_organization.py @@ -0,0 +1,58 @@ +from docusign_admin import ApiClient, UsersApi, OrganizationsApi, IndividualUserDataRedactionRequest, \ + MembershipDataRedactionRequest +from flask import session, request + +from ...ds_config import DS_CONFIG +from app.admin.utils import get_organization_id + + +class Eg010DeleteUserDataFromOrganizationController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + organization_id = get_organization_id() + return { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + "organization_id": organization_id, # Represents your {ORGANIZATION_ID}, + "email": request.form.get("email"), + } + + @staticmethod + def worker(args): + """ + 1. Create an API client with headers + 2. Get user profile data + 3. Delete user data + """ + + access_token = args["access_token"] + org_id = args["organization_id"] + email = args["email"] + + # Create an API client with headers + #ds-snippet-start:Admin10Step2 + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + #ds-snippet-end:Admin10Step2 + + users_api = UsersApi(api_client=api_client) + results = users_api.get_user_ds_profiles_by_email(organization_id=org_id, email=email) + user = results.users[0] + + #ds-snippet-start:Admin10Step3 + organizations_api = OrganizationsApi(api_client=api_client) + user_data_redaction_request = IndividualUserDataRedactionRequest( + user_id=user.id, + memberships=[MembershipDataRedactionRequest(account_id=user.memberships[0].account_id)] + ) + #ds-snippet-end:Admin10Step3 + + #ds-snippet-start:Admin10Step4 + results = organizations_api.redact_individual_user_data(org_id, user_data_redaction_request) + #ds-snippet-end:Admin10Step4 + + return results diff --git a/app/admin/examples/eg011_delete_user_data_from_account.py b/app/admin/examples/eg011_delete_user_data_from_account.py new file mode 100644 index 00000000..40d3f790 --- /dev/null +++ b/app/admin/examples/eg011_delete_user_data_from_account.py @@ -0,0 +1,46 @@ +from docusign_admin import ApiClient, AccountsApi, IndividualMembershipDataRedactionRequest +from flask import session, request + +from ...ds_config import DS_CONFIG + + +class Eg011DeleteUserDataFromAccountController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + return { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + "user_id": request.form.get("user_id"), + } + + @staticmethod + def worker(args): + """ + 1. Create an API client with headers + 2. Delete user data + """ + + access_token = args["access_token"] + account_id = args["account_id"] + user_id = args["user_id"] + + # Create an API client with headers + #ds-snippet-start:Admin11Step2 + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + #ds-snippet-end:Admin11Step2 + + #ds-snippet-start:Admin11Step3 + accounts_api = AccountsApi(api_client=api_client) + membership_redaction_request = IndividualMembershipDataRedactionRequest(user_id=user_id) + #ds-snippet-end:Admin11Step3 + + #ds-snippet-start:Admin11Step4 + results = accounts_api.redact_individual_membership_data(account_id, membership_redaction_request) + #ds-snippet-end:Admin11Step4 + + return results diff --git a/app/admin/examples/eg012_clone_account.py b/app/admin/examples/eg012_clone_account.py new file mode 100644 index 00000000..e2f16247 --- /dev/null +++ b/app/admin/examples/eg012_clone_account.py @@ -0,0 +1,85 @@ +from docusign_admin import ApiClient, ProvisionAssetGroupApi, AssetGroupAccountClone, \ + AssetGroupAccountCloneSourceAccount, AssetGroupAccountCloneTargetAccount, \ + AssetGroupAccountCloneTargetAccountAdmin +from flask import session, request + +from ..utils import get_organization_id +from ...ds_config import DS_CONFIG + + +class Eg012CloneAccountController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + organization_id = get_organization_id() + + return { + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + "organization_id": organization_id, + "source_account_id": request.form.get("source_account_id"), + "target_account_name": request.form.get("target_account_name"), + "target_account_user_name": request.form.get("target_account_user_name"), + "target_account_first_name": request.form.get("target_account_first_name"), + "target_account_last_name": request.form.get("target_account_last_name"), + "target_account_email": request.form.get("target_account_email"), + } + + @staticmethod + def worker(args): + """ + 1. Create an API client with headers + 2. Get the list of eligible accounts + 3. Construct the request body + 4. Clone the account + """ + + access_token = args["access_token"] + + # Create an API client with headers + #ds-snippet-start:Admin12Step2 + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + #ds-snippet-end:Admin12Step2 + + #ds-snippet-start:Admin12Step4 + account_data = AssetGroupAccountClone( + source_account=AssetGroupAccountCloneSourceAccount( + id=args["source_account_id"] + ), + target_account=AssetGroupAccountCloneTargetAccount( + name=args["target_account_name"], + admin=AssetGroupAccountCloneTargetAccountAdmin( + first_name=args["target_account_first_name"], + last_name=args["target_account_last_name"], + email=args["target_account_email"] + ), + country_code="US" + ) + ) + #ds-snippet-end:Admin12Step4 + + #ds-snippet-start:Admin12Step5 + asset_group_api = ProvisionAssetGroupApi(api_client=api_client) + results = asset_group_api.clone_asset_group_account(args["organization_id"], account_data) + #ds-snippet-end:Admin12Step5 + + return results + + @staticmethod + def get_accounts(args): + access_token = args["access_token"] + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + + #ds-snippet-start:Admin12Step3 + asset_group_api = ProvisionAssetGroupApi(api_client=api_client) + accounts = asset_group_api.get_asset_group_accounts(args["organization_id"], compliant=True) + #ds-snippet-end:Admin12Step3 + + return accounts diff --git a/app/admin/examples/eg013_create_account.py b/app/admin/examples/eg013_create_account.py new file mode 100644 index 00000000..c57208fd --- /dev/null +++ b/app/admin/examples/eg013_create_account.py @@ -0,0 +1,89 @@ +from docusign_admin import ApiClient, ProvisionAssetGroupApi, SubAccountCreateRequest, \ + SubAccountCreateRequestSubAccountCreationSubscription, \ + SubAccountCreateRequestSubAccountCreationTargetAccountDetails, \ + SubAccountCreateRequestSubAccountCreationTargetAccountAdmin +from flask import session, request + +from ..utils import get_organization_id +from ...ds_config import DS_CONFIG + + +class Eg013CreateAccountController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + organization_id = get_organization_id() + + return { + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + "organization_id": organization_id, + "base_path": DS_CONFIG["admin_api_client_host"], + "email": request.form.get("email"), + "first_name": request.form.get("first_name"), + "last_name": request.form.get("last_name"), + "subscription_id": session.get("subscription_id"), + "plan_id": session.get("plan_id"), + } + + @staticmethod + def worker(args): + """ + 1. Create an API client with headers + 2. Get the list of eligible accounts + 3. Construct the request body + 4. Create the account + """ + + access_token = args["access_token"] + + # Create an API client with headers + #ds-snippet-start:Admin13Step2 + api_client = ApiClient(host=args["base_path"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + #ds-snippet-end:Admin13Step2 + + #ds-snippet-start:Admin13Step4 + account_data = SubAccountCreateRequest( + subscription_details=SubAccountCreateRequestSubAccountCreationSubscription( + id=args["subscription_id"], + plan_id=args["plan_id"], + modules=[] + ), + target_account=SubAccountCreateRequestSubAccountCreationTargetAccountDetails( + name="CreatedThroughAPI", + country_code="US", + admin=SubAccountCreateRequestSubAccountCreationTargetAccountAdmin( + email=args["email"], + first_name=args["first_name"], + last_name=args["last_name"], + locale="en" + ) + ) + ) + #ds-snippet-end:Admin13Step4 + + #ds-snippet-start:Admin13Step5 + asset_group_api = ProvisionAssetGroupApi(api_client=api_client) + results = asset_group_api.create_asset_group_account(args["organization_id"], account_data) + #ds-snippet-end:Admin13Step5 + + return results + + @staticmethod + def get_organization_plan_items(args): + access_token = args["access_token"] + api_client = ApiClient(host=args["base_path"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + + #ds-snippet-start:Admin13Step3 + asset_group_api = ProvisionAssetGroupApi(api_client=api_client) + plan_items = asset_group_api.get_organization_plan_items(args["organization_id"]) + #ds-snippet-end:Admin13Step3 + + return plan_items diff --git a/app/admin/utils.py b/app/admin/utils.py new file mode 100644 index 00000000..cf87c5f0 --- /dev/null +++ b/app/admin/utils.py @@ -0,0 +1,47 @@ +from docusign_admin import ApiClient, AccountsApi, UsersApi +from flask import session + +from app.ds_config import DS_CONFIG + + +def create_admin_api_client(access_token): + """Create API client and construct API headers""" + + # return api_client + api_client = ApiClient( + host=DS_CONFIG["admin_api_client_host"], + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + return api_client + +def get_organization_id(): + account_id = session["ds_account_id"] + access_token = session["ds_access_token"] + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + + accounts_api = AccountsApi(api_client) + organizations = accounts_api.get_organizations() + org_dict = organizations.to_dict() + first_org = org_dict["organizations"][0] + org_id = first_org["id"] + + return org_id + +def check_user_exists_by_email(user_email): + access_token = session["ds_access_token"] + api_client = ApiClient(host=DS_CONFIG["admin_api_client_host"]) + api_client.set_default_header( + header_name="Authorization", + header_value=f"Bearer {access_token}" + ) + + users_api = UsersApi(api_client) + response = users_api.get_users(organization_id=get_organization_id(), email=user_email) + + return len(response.users) > 0 and response.users[0].user_status != "closed" + diff --git a/app/admin/views/__init__.py b/app/admin/views/__init__.py new file mode 100644 index 00000000..a9230335 --- /dev/null +++ b/app/admin/views/__init__.py @@ -0,0 +1,13 @@ +from .eg001_create_a_new_user import aeg001 +from .eg002_create_active_clm_esign_user import aeg002 +from .eg003_bulk_export_user_data import aeg003 +from .eg004_add_users_via_bulk_import import aeg004 +from .eg005_audit_users import aeg005 +from .eg006_get_user_profile_by_email import aeg006 +from .eg007_get_user_profile_by_user_id import aeg007 +from .eg008_update_user_product_permission_profile import aeg008 +from .eg009_delete_user_product_permission_profile import aeg009 +from .eg010_delete_user_data_from_organization import aeg010 +from .eg011_delete_user_data_from_account import aeg011 +from .eg012_clone_account import aeg012 +from .eg013_create_account import aeg013 diff --git a/app/admin/views/eg001_create_a_new_user.py b/app/admin/views/eg001_create_a_new_user.py new file mode 100644 index 00000000..6e595cfc --- /dev/null +++ b/app/admin/views/eg001_create_a_new_user.py @@ -0,0 +1,80 @@ +"""Example 001: Create a new user""" + +import json +from os import path + +from flask import Blueprint, render_template, request, current_app, session +from docusign_admin.client.api_exception import ApiException + +from app.error_handlers import process_error +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.ds_config import DS_CONFIG +from ...consts import API_TYPE + +from ..examples.eg001_create_a_new_user import Eg001CreateNewUserController + +example_number = 1 +api = API_TYPE["ADMIN"] +eg = f"aeg00{example_number}" # Reference (and URL) for this example +aeg001 = Blueprint(eg, __name__) + +@aeg001.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_user_data(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + controller = Eg001CreateNewUserController() + + # 1. Get required arguments + args = Eg001CreateNewUserController.get_args(request) + + # 2. Call the worker method + try: + results = Eg001CreateNewUserController.worker(controller, args) + current_app.logger.info(f"ID of the created user: {results.id}") + except ApiException as err: + return process_error(err) + + # 3. Render the response + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from eSignUserManagement:createUser method:", + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + +@aeg001.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ + Responds with the form for the example + """ + example = get_example_by_number(session["manifest"], example_number, api) + + args = Eg001CreateNewUserController.get_args(request) + + try: + profiles = Eg001CreateNewUserController.get_permission_profiles(args) + groups = Eg001CreateNewUserController.get_groups(args) + + except ApiException as err: + return process_error(err) + + # Render the response + return render_template( + "admin/eg001_create_a_new_user.html", + title=example["ExampleName"], + example=example, + source_file= "eg001_create_a_new_user.py", + source_url=DS_CONFIG["admin_github_url"] + "eg001_create_a_new_user.py", + documentation=DS_CONFIG["documentation"] + eg, + permission_profiles=profiles, + groups=groups + ) diff --git a/app/admin/views/eg002_create_active_clm_esign_user.py b/app/admin/views/eg002_create_active_clm_esign_user.py new file mode 100644 index 00000000..5ec5575f --- /dev/null +++ b/app/admin/views/eg002_create_active_clm_esign_user.py @@ -0,0 +1,89 @@ +"""Example 002: Create a new active user for CLM and eSignature. """ + +import json +from os import path + +from docusign_admin.client.api_exception import ApiException +from flask import Blueprint, render_template, current_app, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg002_create_active_clm_esign_user import Eg002CreateActiveClmEsignUserController +from ...ds_config import DS_CONFIG +from ...consts import API_TYPE + +example_number = 2 +api = API_TYPE["ADMIN"] +eg = f"aeg00{example_number}" # Reference (and URL) for this example +aeg002 = Blueprint(eg, __name__) + +@aeg002.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def create_active_clm_esign_user(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + controller = Eg002CreateActiveClmEsignUserController() + + # 1. Get required arguments + args = Eg002CreateActiveClmEsignUserController.get_args() + try: + # 2. Call the worker method to get your monitor data + results = Eg002CreateActiveClmEsignUserController.worker(controller, args) + current_app.logger.info(f"""Got your monitor data""") + except ApiException as err: + return process_error(err) + + session["clm_email"] = results["email"] # Save for use by other examples which need an email of CLM user + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from MultiProductUserManagement:addOrUpdateUser method:", + json=json.dumps(json.dumps(results, default=str)) + ) + +@aeg002.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + # Get the required arguments + args = Eg002CreateActiveClmEsignUserController.get_args() + + try: + profiles = Eg002CreateActiveClmEsignUserController.get_permission_profiles(args) + clm_permission_profiles_list = [] + esign_permission_profiles_list = [] + for profile in profiles: + permission_profiles = profile["permission_profiles"] + for permission_profile in permission_profiles: + if profile["product_name"] == "CLM": + clm_permission_profiles_list.append(permission_profile["permission_profile_name"]) + else: + esign_permission_profiles_list.append(permission_profile["permission_profile_name"]) + + ds_groups = Eg002CreateActiveClmEsignUserController.get_groups(args) + ds_groups_dict = ds_groups.to_dict() + ds_groups_list = ds_groups_dict["ds_groups"] + + except ApiException as err: + return process_error(err) + + return render_template( + "admin/eg002_create_active_clm_esign_user.html", + title=example["ExampleName"], + example=example, + source_file= "eg002_create_active_clm_esign_user.py", + source_url=DS_CONFIG["admin_github_url"] + "eg002_create_active_clm_esign_user.py", + documentation=DS_CONFIG["documentation"] + eg, + clm_permission_profiles_list=clm_permission_profiles_list, + esign_permission_profiles_list=esign_permission_profiles_list, + ds_groups=ds_groups_list + ) diff --git a/app/admin/views/eg003_bulk_export_user_data.py b/app/admin/views/eg003_bulk_export_user_data.py new file mode 100644 index 00000000..10e233d4 --- /dev/null +++ b/app/admin/views/eg003_bulk_export_user_data.py @@ -0,0 +1,106 @@ +"""Example 003: Bulk export user data""" + +import json +import time +import os +from os import path +from pathlib import Path + +from flask import Blueprint, render_template, Response, current_app, session +from docusign_admin.client.api_exception import ApiException + +from app.error_handlers import process_error +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.ds_config import DS_CONFIG +from ..examples.eg003_bulk_export_user_data import Eg003BulkExportUserDataController +from ...consts import API_TYPE + +example_number = 3 +api = API_TYPE["ADMIN"] +eg = f"aeg00{example_number}" # Reference (and URL) for this example +aeg003 = Blueprint(eg, __name__) + +@aeg003.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_user_list_data(): + """ + 1. Call the worker method + 2. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Call the worker method + try: + results = Eg003BulkExportUserDataController.worker() + current_app.logger.info(f"User list export ID: {results.id}") + except ApiException as err: + return process_error(err) + + csv_ready = False + while csv_ready == False: + csv_ready = check_if_csv_ready() + time.sleep(5) + + if csv_ready == True: + get_csv() + + file_path = Path("app/admin/examples/exported_user_data.csv").absolute() + + # 2. Render the response + return render_template( + "example_done.html", + get_csv=True, + title=example["ExampleName"], + message=f"User data exported to {file_path}.
Results from UserExport:getUserListExport:", + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + +@aeg003.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ + Responds with the form for the example + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # Render the response + return render_template( + "admin/eg003_bulk_export_user_data.html", + title=example["ExampleName"], + example=example, + source_file= "eg003_bulk_export_user_data.py", + source_url=DS_CONFIG["admin_github_url"] + "eg003_bulk_export_user_data.py", + documentation=DS_CONFIG["documentation"] + eg, + ) + +def check_if_csv_ready(): + """ + 1. Checking if a CSV file exists + 2. Render the response + """ + + # 1. Checking if a CSV file exists + try: + csv_file = Eg003BulkExportUserDataController.get_csv_user_list() + except ApiException as err: + return process_error(err) + + return bool(csv_file) + +def get_csv(): + """ + 1. Getting an existing CSV file + 2. Returns the finished csv file to the user + """ + + # 1. Getting an existing CSV file + try: + csv_file = Eg003BulkExportUserDataController.get_csv_user_list() + except ApiException as err: + return process_error(err) + + results_file = open("app/admin/examples/exported_user_data.csv", "w") + results_file.write(csv_file) + \ No newline at end of file diff --git a/app/admin/views/eg004_add_users_via_bulk_import.py b/app/admin/views/eg004_add_users_via_bulk_import.py new file mode 100644 index 00000000..b31b8cb2 --- /dev/null +++ b/app/admin/views/eg004_add_users_via_bulk_import.py @@ -0,0 +1,117 @@ +"""Example 004: Add users via bulk import""" + +import json +import os +from os import path +import time + +from flask import Blueprint, render_template, request, Response, current_app, session +from docusign_admin.client.api_exception import ApiException + +from app.error_handlers import process_error +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.ds_config import DS_CONFIG +from ..examples.eg004_add_users_via_bulk_import import Eg004AddUsersViaBulkImportController +from ...consts import API_TYPE + +example_number = 4 +api = API_TYPE["ADMIN"] +eg = f"aeg00{example_number}" # Reference(and URL) for this example +aeg004 = Blueprint(eg, __name__) + + +@aeg004.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def add_users_via_bulk_import(): + """ + 1. Call the worker method + 2. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + controller = Eg004AddUsersViaBulkImportController() + + # 1. Call the worker method + try: + results = Eg004AddUsersViaBulkImportController.worker(controller, request) + current_app.logger.info(f"Bulk import request ID: {results.id}") + except ApiException as err: + return process_error(err) + + # 2. Render the response + return render_template( + "example_done.html", + check_status = True, + title=example["ExampleName"], + message=f"Results from UserImport:addBulkUserImport method:", + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + +@aeg004.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ + Responds with the form for the example + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # Render the response + return render_template( + "admin/eg004_add_users_via_bulk_import.html", + title=example["ExampleName"], + example=example, + source_file="eg004_add_users_via_bulk_import.py", + source_url=DS_CONFIG["admin_github_url"] + "eg004_add_users_via_bulk_import.py", + documentation=DS_CONFIG["documentation"] + eg, + ) + +@aeg004.route("/eg004examplecsv", methods=["GET"]) +@authenticate(eg=eg, api=api) +def get_csv(): + """ + 1. Creates an example of a CSV file + 2. Returns an example of a CSV file to the user + """ + + # 1. Creates an example of a CSV file + csv_file = Eg004AddUsersViaBulkImportController.get_example_csv() + + # 2. Returns an example of a CSV file to the user + return Response( + csv_file, + mimetype="text/csv", + headers={ + "Content-disposition":"attachment; filename=bulk_import_demo.csv" + } + ) + +@aeg004.route("/eg004check", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def check_if_request_ready(): + """ + 1. Checking if the request is complete + 2. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # Check if request is complete + try: + results = Eg004AddUsersViaBulkImportController.check_status() + except ApiException as err: + return process_error(err) + + if not results: + return render_template( + "admin/eg004_file_state.html", + ) + else: + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"Results from UserImport:getBulkUserImportRequest method:", + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + \ No newline at end of file diff --git a/app/admin/views/eg005_audit_users.py b/app/admin/views/eg005_audit_users.py new file mode 100644 index 00000000..4e9ef173 --- /dev/null +++ b/app/admin/views/eg005_audit_users.py @@ -0,0 +1,62 @@ +"""Example 005: Audit users. """ + +import json +from os import path + +from docusign_admin.client.api_exception import ApiException +from flask import Blueprint, render_template, current_app, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg005_audit_users import Eg005AuditUsersController +from ...ds_config import DS_CONFIG +from ...consts import API_TYPE + +example_number = 5 +api = API_TYPE["ADMIN"] +eg = f"aeg00{example_number}" # Reference (and URL) for this example +aeg005 = Blueprint(eg, __name__) + +@aeg005.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def audit_users(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg005AuditUsersController.get_args() + try: + # 2. Call the worker method to get your monitor data + results = Eg005AuditUsersController.worker(args) + current_app.logger.info(f"""Auditing users""") + except ApiException as err: + return process_error(err) + + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from eSignUserManagement:getUserProfiles method:", + json=json.dumps(json.dumps(results, default=str)) + ) + +@aeg005.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "admin/eg005_audit_users.html", + title=example["ExampleName"], + example=example, + source_file= "eg005_audit_users.py", + source_url=DS_CONFIG["admin_github_url"] + "eg005_audit_users.py", + documentation=DS_CONFIG["documentation"] + eg, + ) + diff --git a/app/admin/views/eg006_get_user_profile_by_email.py b/app/admin/views/eg006_get_user_profile_by_email.py new file mode 100644 index 00000000..8265edbe --- /dev/null +++ b/app/admin/views/eg006_get_user_profile_by_email.py @@ -0,0 +1,60 @@ +"""Example 006: Get user profile data by email. """ + +import json + +from docusign_admin.client.api_exception import ApiException +from flask import Blueprint, render_template, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg006_get_user_profile_by_email import Eg006GetUserProfileByEmailController +from ...ds_config import DS_CONFIG +from ...consts import API_TYPE + +example_number = 6 +api = API_TYPE["ADMIN"] +eg = f"aeg00{example_number}" # Reference (and URL) for this example +aeg006 = Blueprint(eg, __name__) + +@aeg006.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def audit_users(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg006GetUserProfileByEmailController.get_args() + try: + # 2. Call the worker method to get user profile data by email + results = Eg006GetUserProfileByEmailController.worker(args) + except ApiException as err: + return process_error(err) + + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from MultiProductUserManagement:getUserDSProfilesByEmail method:", + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + +@aeg006.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + f"admin/eg006_get_user_profile_by_email.html", + title=example["ExampleName"], + example=example, + source_file=f"eg006_get_user_profile_by_email.py", + source_url=DS_CONFIG["admin_github_url"] + f"eg006_get_user_profile_by_email.py", + documentation=DS_CONFIG["documentation"] + eg, + ) + diff --git a/app/admin/views/eg007_get_user_profile_by_user_id.py b/app/admin/views/eg007_get_user_profile_by_user_id.py new file mode 100644 index 00000000..7031d72e --- /dev/null +++ b/app/admin/views/eg007_get_user_profile_by_user_id.py @@ -0,0 +1,60 @@ +"""Example 007: Get user profile data by user ID. """ + +import json + +from docusign_admin.client.api_exception import ApiException +from flask import Blueprint, render_template, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg007_get_user_profile_by_user_id import Eg007GetUserProfileByUserIdController +from ...ds_config import DS_CONFIG +from ...consts import API_TYPE + +example_number = 7 +api = API_TYPE["ADMIN"] +eg = f"aeg00{example_number}" # Reference (and URL) for this example +aeg007 = Blueprint(eg, __name__) + +@aeg007.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def audit_users(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg007GetUserProfileByUserIdController.get_args() + try: + # 2. Call the worker method to get user profile data by user ID + results = Eg007GetUserProfileByUserIdController.worker(args) + except ApiException as err: + return process_error(err) + + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from MultiProductUserManagement:getUserDSProfile method:", + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + +@aeg007.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + f"admin/eg007_get_user_profile_by_user_id.html", + title=example["ExampleName"], + example=example, + source_file=f"eg007_get_user_profile_by_user_id.py", + source_url=DS_CONFIG["admin_github_url"] + f"eg007_get_user_profile_by_user_id.py", + documentation=DS_CONFIG["documentation"] + eg, + ) + diff --git a/app/admin/views/eg008_update_user_product_permission_profile.py b/app/admin/views/eg008_update_user_product_permission_profile.py new file mode 100644 index 00000000..6779379c --- /dev/null +++ b/app/admin/views/eg008_update_user_product_permission_profile.py @@ -0,0 +1,125 @@ +"""Example 008: Update user product permission profiles using an email address. """ + +import json + +from docusign_admin.client.api_exception import ApiException +from flask import Blueprint, render_template, request, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg008_update_user_product_permission_profile import Eg008UpdateUserProductPermissionProfileController +from ...ds_config import DS_CONFIG +from ..utils import check_user_exists_by_email +from ...consts import API_TYPE + +example_number = 8 +api = API_TYPE["ADMIN"] +eg = f"aeg00{example_number}" # Reference (and URL) for this example +aeg008 = Blueprint(eg, __name__) + +@aeg008.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def create_active_clm_esign_user(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + if "clm_email" in session and check_user_exists_by_email(session["clm_email"]): + controller = Eg008UpdateUserProductPermissionProfileController() + + # 1. Get required arguments + args = Eg008UpdateUserProductPermissionProfileController.get_args() + profiles = Eg008UpdateUserProductPermissionProfileController.get_permission_profiles() + + for profile in profiles: + if profile["product_id"] == args["product_id"]: + if profile["product_name"] == "CLM": + args["permission_profile_id"] = request.form.get("clm_permission_profile") + else: + args["permission_profile_id"] = request.form.get("esign_permission_profile") + + try: + # 2. Call the worker method + results = Eg008UpdateUserProductPermissionProfileController.worker(controller, args) + except ApiException as err: + return process_error(err) + + template = render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from MultiProductUserManagement:addUserProductPermissionProfilesByEmail method:", + json=json.dumps(json.dumps(results, default=str)) + ) + else: + template = render_template( + f"admin/eg008_create_active_clm_esign_user.html", + title=example["ExampleName"], + example=example, + email_ok=False, + source_file=f"eg008_create_active_clm_esign_user.py", + source_url=DS_CONFIG["admin_github_url"] + f"eg008_create_active_clm_esign_user.py", + documentation=DS_CONFIG["documentation"] + eg + ) + + return template + +@aeg008.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + if "clm_email" in session and check_user_exists_by_email(session["clm_email"]): + try: + profiles = Eg008UpdateUserProductPermissionProfileController.get_permission_profiles() + clm_permission_profiles_list = [] + esign_permission_profiles_list = [] + clm_product_id = "" + esign_product_id = "" + products_list = [] + for profile in profiles: + permission_profiles = profile["permission_profiles"] + for permission_profile in permission_profiles: + if profile["product_name"] == "CLM": + clm_permission_profiles_list.append(permission_profile) + clm_product_id = profile["product_id"] + else: + esign_permission_profiles_list.append(permission_profile) + esign_product_id = profile["product_id"] + + products_list.append({"product_id": clm_product_id, "product_name": "CLM"}) + products_list.append({"product_id": esign_product_id, "product_name": "eSignature"}) + + except ApiException as err: + return process_error(err) + + template = render_template( + f"admin/eg008_update_user_product_permission_profile.html", + title=example["ExampleName"], + example=example, + email_ok="clm_email" in session, + source_file=f"eg008_update_user_product_permission_profile.py", + source_url=DS_CONFIG["admin_github_url"] + f"eg008_update_user_product_permission_profile.py", + documentation=DS_CONFIG["documentation"] + eg, + product_list=products_list, + email=session["clm_email"], + clm_permission_profiles_list=clm_permission_profiles_list, + esign_permission_profiles_list=esign_permission_profiles_list + ) + else: + template = render_template( + f"admin/eg008_update_user_product_permission_profile.html", + title=example["ExampleName"], + example=example, + email_ok=False, + source_file=f"eg008_update_user_product_permission_profile.py", + source_url=DS_CONFIG["admin_github_url"] + f"eg008_update_user_product_permission_profile.py", + documentation=DS_CONFIG["documentation"] + eg + ) + + return template diff --git a/app/admin/views/eg009_delete_user_product_permission_profile.py b/app/admin/views/eg009_delete_user_product_permission_profile.py new file mode 100644 index 00000000..b93e458e --- /dev/null +++ b/app/admin/views/eg009_delete_user_product_permission_profile.py @@ -0,0 +1,115 @@ +"""Example 009: Delete user product permission profiles using an email address. """ + +import json + +from docusign_admin.client.api_exception import ApiException +from flask import Blueprint, render_template, current_app, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg009_delete_user_product_permission_profile import Eg009DeleteUserProductPermissionProfileController +from ...ds_config import DS_CONFIG +from ..utils import check_user_exists_by_email +from ...consts import API_TYPE + +example_number = 9 +api = API_TYPE["ADMIN"] +eg = f"aeg00{example_number}" # Reference (and URL) for this example +aeg009 = Blueprint(eg, __name__) + +@aeg009.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def create_active_clm_esign_user(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + if "clm_email" in session and check_user_exists_by_email(session["clm_email"]): + controller = Eg009DeleteUserProductPermissionProfileController() + + # 1. Get required arguments + args = Eg009DeleteUserProductPermissionProfileController.get_args() + try: + # 2. Call the worker method + results = Eg009DeleteUserProductPermissionProfileController.worker(controller, args) + except ApiException as err: + return process_error(err) + + template = render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from MultiProductUserManagement:removeUserProductPermission method:", + json=json.dumps(json.dumps(results, default=str)) + ) + else: + template = render_template( + f"admin/eg009_delete_user_product_permission_profile.html", + title=example["ExampleName"], + example=example, + source_file=f"eg009_delete_user_product_permission_profile.py", + source_url=DS_CONFIG["admin_github_url"] + f"eg009_delete_user_product_permission_profile.py", + documentation=DS_CONFIG["documentation"] + eg + ) + + return template + +@aeg009.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + if "clm_email" in session and check_user_exists_by_email(session["clm_email"]): + try: + profiles = Eg009DeleteUserProductPermissionProfileController.get_permission_profiles_by_email() + permission_profile_list = [] + clm_product_id = None + clm_permission_profile_name = None + esign_product_id = None + esign_permission_profile_name = None + for profile in profiles: + permission_profiles = profile["permission_profiles"] + for permission_profile in permission_profiles: + if profile["product_name"] == "CLM": + clm_permission_profile_name = permission_profile["permission_profile_name"] + clm_product_id = profile["product_id"] + else: + esign_permission_profile_name = permission_profile["permission_profile_name"] + esign_product_id = profile["product_id"] + + if clm_product_id is not None: + permission_profile_list.append({"product_id": clm_product_id, "permission_name": f"CLM - {clm_permission_profile_name}"}) + + if esign_product_id is not None: + permission_profile_list.append({"product_id": esign_product_id, "permission_name": f"eSignature - {esign_permission_profile_name}"}) + + except ApiException as err: + return process_error(err) + + template = render_template( + f"admin/eg009_delete_user_product_permission_profile.html", + title=example["ExampleName"], + example=example, + email_ok="clm_email" in session, + source_file=f"eg009_delete_user_product_permission_profile.py", + source_url=DS_CONFIG["admin_github_url"] + f"eg009_delete_user_product_permission_profile.py", + documentation=DS_CONFIG["documentation"] + eg, + email=session["clm_email"], + permission_profile_list=permission_profile_list + ) + else: + template = render_template( + f"admin/eg009_delete_user_product_permission_profile.html", + title=example["ExampleName"], + example=example, + source_file=f"eg009_delete_user_product_permission_profile.py", + source_url=DS_CONFIG["admin_github_url"] + f"eg009_delete_user_product_permission_profile.py", + documentation=DS_CONFIG["documentation"] + eg + ) + + return template diff --git a/app/admin/views/eg010_delete_user_data_from_organization.py b/app/admin/views/eg010_delete_user_data_from_organization.py new file mode 100644 index 00000000..caea398c --- /dev/null +++ b/app/admin/views/eg010_delete_user_data_from_organization.py @@ -0,0 +1,62 @@ +"""Example 010: Delete user data from an account as an organization admin. """ + +import json + +from docusign_admin.client.api_exception import ApiException +from flask import Blueprint, render_template, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg010_delete_user_data_from_organization import Eg010DeleteUserDataFromOrganizationController +from ...ds_config import DS_CONFIG +from ...consts import API_TYPE + +example_number = 10 +api = API_TYPE["ADMIN"] +eg = f"aeg0{example_number}" # Reference (and URL) for this example +aeg010 = Blueprint(eg, __name__) + + +@aeg010.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def audit_users(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg010DeleteUserDataFromOrganizationController.get_args() + try: + # 2. Call the worker method to delete user data by email + results = Eg010DeleteUserDataFromOrganizationController.worker(args) + except ApiException as err: + return process_error(err) + + return render_template( + "example_done.html", + title=example["ExampleName"], + message=example["ResultsPageText"], + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + + +@aeg010.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "admin/eg010_delete_user_data_from_organization.html", + title=example["ExampleName"], + example=example, + source_file="eg010_delete_user_data_from_organization.py", + source_url=DS_CONFIG["admin_github_url"] + "eg010_delete_user_data_from_organization.py", + documentation=DS_CONFIG["documentation"] + eg, + ) + diff --git a/app/admin/views/eg011_delete_user_data_from_account.py b/app/admin/views/eg011_delete_user_data_from_account.py new file mode 100644 index 00000000..a5cf43d5 --- /dev/null +++ b/app/admin/views/eg011_delete_user_data_from_account.py @@ -0,0 +1,62 @@ +"""Example 011: Delete user data from an account as an account admin. """ + +import json + +from docusign_admin.client.api_exception import ApiException +from flask import Blueprint, render_template, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg011_delete_user_data_from_account import Eg011DeleteUserDataFromAccountController +from ...ds_config import DS_CONFIG +from ...consts import API_TYPE + +example_number = 11 +api = API_TYPE["ADMIN"] +eg = f"aeg0{example_number}" # Reference (and URL) for this example +aeg011 = Blueprint(eg, __name__) + + +@aeg011.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def audit_users(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg011DeleteUserDataFromAccountController.get_args() + try: + # 2. Call the worker method to delete user data by user ID + results = Eg011DeleteUserDataFromAccountController.worker(args) + except ApiException as err: + return process_error(err) + + return render_template( + "example_done.html", + title=example["ExampleName"], + message=example["ResultsPageText"], + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + + +@aeg011.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "admin/eg011_delete_user_data_from_account.html", + title=example["ExampleName"], + example=example, + source_file="eg011_delete_user_data_from_account.py", + source_url=DS_CONFIG["admin_github_url"] + "eg011_delete_user_data_from_account.py", + documentation=DS_CONFIG["documentation"] + eg, + ) + diff --git a/app/admin/views/eg012_clone_account.py b/app/admin/views/eg012_clone_account.py new file mode 100644 index 00000000..c4c2289c --- /dev/null +++ b/app/admin/views/eg012_clone_account.py @@ -0,0 +1,70 @@ +"""Example 012: How to clone an account. """ + +import json + +from docusign_admin.client.api_exception import ApiException +from flask import Blueprint, render_template, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg012_clone_account import Eg012CloneAccountController +from ...ds_config import DS_CONFIG +from ...consts import API_TYPE + +example_number = 12 +api = API_TYPE["ADMIN"] +eg = f"aeg0{example_number}" # Reference (and URL) for this example +aeg012 = Blueprint(eg, __name__) + + +@aeg012.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def audit_users(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg012CloneAccountController.get_args() + try: + # 2. Call the worker method to clone the account + results = Eg012CloneAccountController.worker(args) + except ApiException as err: + return process_error(err) + + return render_template( + "example_done.html", + title=example["ExampleName"], + message=example["ResultsPageText"], + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + + +@aeg012.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = Eg012CloneAccountController.get_args() + + try: + accounts = Eg012CloneAccountController.get_accounts(args) + except ApiException as err: + process_error(err) + + return render_template( + "admin/eg012_clone_account.html", + title=example["ExampleName"], + example=example, + source_file="eg012_clone_account.py", + source_url=DS_CONFIG["admin_github_url"] + "eg012_clone_account.py", + documentation=DS_CONFIG["documentation"] + eg, + accounts=accounts.asset_group_accounts + ) + diff --git a/app/admin/views/eg013_create_account.py b/app/admin/views/eg013_create_account.py new file mode 100644 index 00000000..0be7df3e --- /dev/null +++ b/app/admin/views/eg013_create_account.py @@ -0,0 +1,72 @@ +"""Example 013: How to create an account. """ + +import json + +from docusign_admin.client.api_exception import ApiException +from flask import Blueprint, render_template, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg013_create_account import Eg013CreateAccountController +from ...ds_config import DS_CONFIG +from ...consts import API_TYPE + +example_number = 13 +api = API_TYPE["ADMIN"] +eg = f"aeg0{example_number}" # Reference (and URL) for this example +aeg013 = Blueprint(eg, __name__) + + +@aeg013.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def create_account(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg013CreateAccountController.get_args() + try: + # 2. Call the worker method to create an account + results = Eg013CreateAccountController.worker(args) + except ApiException as err: + return process_error(err) + + return render_template( + "example_done.html", + title=example["ExampleName"], + message=example["ResultsPageText"], + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + + +@aeg013.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = Eg013CreateAccountController.get_args() + + try: + plan_items = Eg013CreateAccountController.get_organization_plan_items(args) + session["subscription_id"] = plan_items[0].subscription_id + session["plan_id"] = plan_items[0].plan_id + + except ApiException as err: + process_error(err) + + return render_template( + "admin/eg013_create_account.html", + title=example["ExampleName"], + example=example, + source_file="eg013_create_account.py", + source_url=DS_CONFIG["admin_github_url"] + "eg013_create_account.py", + documentation=DS_CONFIG["documentation"] + eg + ) + diff --git a/app/api_type.py b/app/api_type.py new file mode 100644 index 00000000..0bc16fb5 --- /dev/null +++ b/app/api_type.py @@ -0,0 +1 @@ +EXAMPLES_API_TYPE ={'Rooms': False, 'ESignature': True, 'Click': False, 'Monitor': False, 'Admin': False, 'Notary': False} \ No newline at end of file diff --git a/app/click/examples/__init__.py b/app/click/examples/__init__.py deleted file mode 100644 index c5ea6a03..00000000 --- a/app/click/examples/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .eg001_create_clickwrap import eg001 -from .eg002_activate_clickwrap import eg002 -from .eg003_create_new_clickwrap_version import eg003 -from .eg004_list_clickwraps import eg004 -from .eg005_clickwrap_responses import eg005 diff --git a/app/click/examples/eg001_create_clickwrap/controller.py b/app/click/examples/eg001_create_clickwrap.py similarity index 79% rename from app/click/examples/eg001_create_clickwrap/controller.py rename to app/click/examples/eg001_create_clickwrap.py index 7e550864..46d7920e 100644 --- a/app/click/examples/eg001_create_clickwrap/controller.py +++ b/app/click/examples/eg001_create_clickwrap.py @@ -5,12 +5,12 @@ Document from flask import session, request -from ....consts import demo_docs_path -from ....ds_config import DS_CONFIG -from ...utils import create_click_api_client +from ...consts import demo_docs_path +from ...ds_config import DS_CONFIG +from ..utils import create_click_api_client -class Eg001Controller: +class Eg001CreateClickwrapController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -29,19 +29,21 @@ def worker(args): 4. Create a clickwrap request model 5. Create a clickwrap using SDK """ - # Step 1. Create an API client with headers + # Create an API client with headers + #ds-snippet-start:Click1Step2 api_client = create_click_api_client( access_token=args["access_token"] ) - - # Step 2. Create a display settings model + #ds-snippet-end + + #ds-snippet-start:Click1Step3 + # Create a display settings model display_settings = DisplaySettings( consent_button_text="I Agree", display_name="Terms of Service", downloadable=True, format="modal", must_read=True, - must_view=True, require_accept=True, document_display="document" ) @@ -53,7 +55,7 @@ def worker(args): doc_docx_bytes = file.read() doc_b64 = base64.b64encode(doc_docx_bytes).decode("ascii") - # Step 3. Create a document model. + # Create a document model. document = Document( # Create the DocuSign document object document_base64=doc_b64, document_name="Terms of Service", # Can be different from actual file name @@ -61,19 +63,22 @@ def worker(args): order=0 ) - # Step 4. Create a clickwrap request model + # Create a clickwrap request model clickwrap_request = ClickwrapRequest( display_settings=display_settings, documents=[document, ], name=args.get("clickwrap_name"), require_reacceptance=True ) + #ds-snippet-end:Click1Step3 - # Step 5. Create a clickwrap using SDK + # Create a clickwrap using SDK + #ds-snippet-start:Click1Step4 accounts_api = AccountsApi(api_client) response = accounts_api.create_clickwrap( clickwrap_request=clickwrap_request, account_id=args["account_id"] ) + #ds-snippet-end:Click1Step4 return response diff --git a/app/click/examples/eg001_create_clickwrap/__init__.py b/app/click/examples/eg001_create_clickwrap/__init__.py deleted file mode 100644 index 11c5cf04..00000000 --- a/app/click/examples/eg001_create_clickwrap/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg001 diff --git a/app/click/examples/eg002_activate_clickwrap.py b/app/click/examples/eg002_activate_clickwrap.py new file mode 100644 index 00000000..3eb6e65e --- /dev/null +++ b/app/click/examples/eg002_activate_clickwrap.py @@ -0,0 +1,77 @@ +from docusign_click import AccountsApi, ClickwrapRequest +from flask import session, request +import ast + +from ..utils import create_click_api_client + + +class Eg002ActivateClickwrapController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + return { + "account_id": session.get("ds_account_id"), # Represents your {ACCOUNT_ID} + "access_token": session.get("ds_access_token"), # Represents your {ACCESS_TOKEN} + "clickwrap": request.form.get("clickwrap"), + "statuses": ["inactive", "draft"] + } + + @staticmethod + def get_inactive_clickwraps(args): + """ + 1. Create an API client with hheaders + 2. Get a list of inactive clickwraps + """ + # Step 1. Create an API client with headers + api_client = create_click_api_client( + access_token=args["access_token"] + ) + + # Step 2. Get a list of inactive clickwraps + accounts_api = AccountsApi(api_client) + clickwraps = [] + + for status in args["statuses"]: + response = accounts_api.get_clickwraps( + account_id=args["account_id"], + status=status + ) + clickwraps += response.clickwraps + + return {"clickwraps": clickwraps} + + @staticmethod + def worker(args): + """ + 1. Create an API client with headers + 2. Create a clickwrap request model + 3. Update a clickwrap using SDK + """ + # Step 1. Create an API client with headers + #ds-snippet-start:Click2Step2 + api_client = create_click_api_client( + access_token=args["access_token"] + ) + #ds-snippet-end:Click2Step2 + + # Step 2. Create a clickwrap request model + #ds-snippet-start:Click2Step3 + clickwrap_request = ClickwrapRequest(status="active") + #ds-snippet-end:Click2Step3 + + # Step 3. Update a clickwrap using SDK + #ds-snippet-start:Click2Step4 + accounts_api = AccountsApi(api_client) + #ds-snippet-end:Click2Step4 + clickwrap = ast.literal_eval(args["clickwrap"]) + print(type(clickwrap)) + #ds-snippet-start:Click2Step4 + response = accounts_api.update_clickwrap_version( + account_id=args["account_id"], + clickwrap_id=clickwrap["clickwrap_id"], + version_id=clickwrap["version_number"], + clickwrap_request=clickwrap_request, + ) + #ds-snippet-end:Click2Step4 + + return response diff --git a/app/click/examples/eg002_activate_clickwrap/__init__.py b/app/click/examples/eg002_activate_clickwrap/__init__.py deleted file mode 100644 index 1b2ca36d..00000000 --- a/app/click/examples/eg002_activate_clickwrap/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg002 diff --git a/app/click/examples/eg002_activate_clickwrap/controller.py b/app/click/examples/eg002_activate_clickwrap/controller.py deleted file mode 100644 index 87c697eb..00000000 --- a/app/click/examples/eg002_activate_clickwrap/controller.py +++ /dev/null @@ -1,42 +0,0 @@ -from docusign_click import AccountsApi, ClickwrapRequest -from flask import session - -from ...utils import create_click_api_client - - -class Eg002Controller: - @staticmethod - def get_args(): - """Get required session and request arguments""" - return { - "account_id": session.get("ds_account_id"), # Represents your {ACCOUNT_ID} - "access_token": session.get("ds_access_token"), # Represents your {ACCESS_TOKEN} - "clickwrap_id": session.get("clickwrap_id"), - "clickwrap_name": session.get("clickwrap_name"), - } - - @staticmethod - def worker(args): - """ - 1. Create an API client with headers - 2. Create a clickwrap request model - 3. Update a clickwrap using SDK - """ - # Step 1. Create an API client with headers - api_client = create_click_api_client( - access_token=args["access_token"] - ) - - # Step 2. Create a clickwrap request model - clickwrap_request = ClickwrapRequest(status="active") - - # Step 3. Update a clickwrap using SDK - accounts_api = AccountsApi(api_client) - response = accounts_api.update_clickwrap_version( - account_id=args["account_id"], - clickwrap_id=args["clickwrap_id"], - clickwrap_request=clickwrap_request, - version_id="1" - ) - - return response diff --git a/app/click/examples/eg002_activate_clickwrap/views.py b/app/click/examples/eg002_activate_clickwrap/views.py deleted file mode 100644 index 0b4f82d0..00000000 --- a/app/click/examples/eg002_activate_clickwrap/views.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Example 002: Activating a clickwrap""" - -from os import path -import json - -from docusign_click.client.api_exception import ApiException -from flask import render_template, current_app, Blueprint, session - -from .controller import Eg002Controller -from app.docusign import authenticate -from app.ds_config import DS_CONFIG -from app.error_handlers import process_error - -eg = "eg002" # Reference (and URL) for this example -eg002 = Blueprint("eg002", __name__) - - -@eg002.route("/eg002", methods=["POST"]) -@authenticate(eg=eg) -def activate_clickwrap(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render the response - """ - # 1. Get required arguments - args = Eg002Controller.get_args() - - try: - # 2. Call the worker method to create a new clickwrap - results = Eg002Controller.worker(args) - current_app.logger.info( - f"""The clickwrap "{args['clickwrap_name']}" has been activated!""" - ) - except ApiException as err: - return process_error(err) - - # Save for use by other examples which need an clickwrap params. - session["clickwrap_is_active"] = True - - # 3. Render the response - return render_template( - "example_done.html", - title="Activating a clickwrap", - h1="Activating a clickwrap", - message=f"""The clickwrap "{args['clickwrap_name']}" has been activated!""", - json=json.dumps(json.dumps(results.to_dict(), default=str)) - ) - - -@eg002.route("/eg002", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - return render_template( - "eg002_activate_clickwrap.html", - title="Activating a clickwrap", - clickwrap_ok="clickwrap_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename( - path.dirname(__file__)) + "/controller.py", - ) diff --git a/app/click/examples/eg003_create_new_clickwrap_version/controller.py b/app/click/examples/eg003_create_new_clickwrap_version.py similarity index 76% rename from app/click/examples/eg003_create_new_clickwrap_version/controller.py rename to app/click/examples/eg003_create_new_clickwrap_version.py index 9796cf6e..c3ebe4d7 100644 --- a/app/click/examples/eg003_create_new_clickwrap_version/controller.py +++ b/app/click/examples/eg003_create_new_clickwrap_version.py @@ -3,22 +3,22 @@ from docusign_click import AccountsApi, ClickwrapRequest, DisplaySettings, \ Document -from flask import session +from flask import session, request -from ....consts import demo_docs_path -from ....ds_config import DS_CONFIG -from ...utils import create_click_api_client +from ...consts import demo_docs_path +from ...ds_config import DS_CONFIG +from ..utils import create_click_api_client -class Eg003Controller: +class Eg003CrateNewClickwrapVersionController: @staticmethod def get_args(): """Get required session and request arguments""" return { "account_id": session.get("ds_account_id"), # Represents your {ACCOUNT_ID} "access_token": session.get("ds_access_token"), # Represents your {ACCESS_TOKEN} - "clickwrap_id": session.get("clickwrap_id"), - "clickwrap_name": session.get("clickwrap_name"), + "clickwrap_id": request.form.get("clickwrap_id"), + "clickwrap_name": request.form.get("clickwrap_name"), } @staticmethod @@ -30,19 +30,21 @@ def worker(args): 4. Create a clickwrap request model 5. Create a new clickwrap version using SDK """ - # Step 1. Create an API client with headers + # Create an API client with headers + #ds-snippet-start:Click3Step2 api_client = create_click_api_client( access_token=args["access_token"] ) + #ds-snippet-end - # Step 2. Create a display settings model + # Create a display settings model + #ds-snippet-start:Click3Step3 display_settings = DisplaySettings( consent_button_text="I Agree", display_name=f"{args.get('clickwrap_name')} v2", downloadable=False, format="modal", must_read=True, - must_view=False, require_accept=False, document_display="document", send_to_email=False @@ -55,7 +57,7 @@ def worker(args): doc_docx_bytes = file.read() doc_b64 = base64.b64encode(doc_docx_bytes).decode("ascii") - # Step 3. Create a document model. + # Create a document model. document = Document( # Create the DocuSign document object document_base64=doc_b64, document_name="Terms of Service", # Can be different from actual file name @@ -63,7 +65,7 @@ def worker(args): order=0 ) - # Step 4. Create a clickwrap request model + # Create a clickwrap request model clickwrap_request = ClickwrapRequest( display_settings=display_settings, documents=[document, ], @@ -71,13 +73,16 @@ def worker(args): require_reacceptance=True, status="active" ) + #ds-snippet-end - # Step 5. Create a new clickwrap version using SDK + # Create a new clickwrap version using SDK + #ds-snippet-start:Click3Step4 accounts_api = AccountsApi(api_client) response = accounts_api.create_clickwrap_version( account_id=args["account_id"], clickwrap_id=args["clickwrap_id"], clickwrap_request=clickwrap_request, ) + #ds-snippet-end return response diff --git a/app/click/examples/eg003_create_new_clickwrap_version/__init__.py b/app/click/examples/eg003_create_new_clickwrap_version/__init__.py deleted file mode 100644 index c5813bf4..00000000 --- a/app/click/examples/eg003_create_new_clickwrap_version/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg003 diff --git a/app/click/examples/eg003_create_new_clickwrap_version/views.py b/app/click/examples/eg003_create_new_clickwrap_version/views.py deleted file mode 100644 index 482b077c..00000000 --- a/app/click/examples/eg003_create_new_clickwrap_version/views.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Example 003: Creating a new clickwrap version""" - -from os import path -import json - -from docusign_click.client.api_exception import ApiException -from flask import render_template, current_app, Blueprint, session - -from .controller import Eg003Controller -from app.docusign import authenticate -from app.ds_config import DS_CONFIG -from app.error_handlers import process_error - -eg = "eg003" # Reference (and URL) for this example -eg003 = Blueprint("eg003", __name__) - - -@eg003.route("/eg003", methods=["POST"]) -@authenticate(eg=eg) -def create_new_clickwrap_version(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render the response - """ - # 1. Get required arguments - args = Eg003Controller.get_args() - - try: - # 2. Call the worker method to create a new clickwrap version - results = Eg003Controller.worker(args) - current_app.logger.info( - f"""The 2nd version of clickwrap "{args['clickwrap_name']}" has been created!""" - ) - except ApiException as err: - return process_error(err) - - # 3. Render the response - return render_template( - "example_done.html", - title="Creating a new clickwrap version", - h1="Creating a new clickwrap version", - message=f"""The 2nd version of clickwrap "{args['clickwrap_name']}" has been created!""", - json=json.dumps(json.dumps(results.to_dict(), default=str)) - ) - - -@eg003.route("/eg003", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - return render_template( - "eg003_create_new_clickwrap_version.html", - title="Creating a new clickwrap version", - clickwrap_ok="clickwrap_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename( - path.dirname(__file__)) + "/controller.py", - ) diff --git a/app/click/examples/eg004_list_clickwraps/controller.py b/app/click/examples/eg004_list_clickwraps.py similarity index 65% rename from app/click/examples/eg004_list_clickwraps/controller.py rename to app/click/examples/eg004_list_clickwraps.py index 99075fff..2c986bc5 100644 --- a/app/click/examples/eg004_list_clickwraps/controller.py +++ b/app/click/examples/eg004_list_clickwraps.py @@ -1,10 +1,10 @@ from docusign_click import AccountsApi, ClickwrapRequest from flask import session -from ...utils import create_click_api_client +from ..utils import create_click_api_client -class Eg004Controller: +class Eg004ListClickwrapsController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -16,18 +16,22 @@ def get_args(): @staticmethod def worker(args): """ - 1. Create an API client with headers - 2. Get a list of all clickwraps + Create an API client with headers + Get a list of all elastic templates """ - # Step 1. Create an API client with headers + # Create an API client with headers + #ds-snippet-start:Click4Step2 api_client = create_click_api_client( access_token=args["access_token"] ) + #ds-snippet-end - # Step 2. Get a list of all clickwraps + # Get a list of all elastic templates + #ds-snippet-start:Click4Step3 accounts_api = AccountsApi(api_client) response = accounts_api.get_clickwraps( account_id=args["account_id"] ) + #ds-snippet-end return response diff --git a/app/click/examples/eg004_list_clickwraps/__init__.py b/app/click/examples/eg004_list_clickwraps/__init__.py deleted file mode 100644 index 4af46e78..00000000 --- a/app/click/examples/eg004_list_clickwraps/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg004 diff --git a/app/click/examples/eg004_list_clickwraps/views.py b/app/click/examples/eg004_list_clickwraps/views.py deleted file mode 100644 index ad104db2..00000000 --- a/app/click/examples/eg004_list_clickwraps/views.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Example 004: Getting a list of clickwraps""" - -from os import path -import json - -from docusign_click.client.api_exception import ApiException -from flask import render_template, current_app, Blueprint, session - -from .controller import Eg004Controller -from app.docusign import authenticate -from app.ds_config import DS_CONFIG -from app.error_handlers import process_error - -eg = "eg004" # Reference (and URL) for this example -eg004 = Blueprint("eg004", __name__) - - -@eg004.route("/eg004", methods=["POST"]) -@authenticate(eg=eg) -def clickwrap_list(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render the response - """ - # 1. Get required arguments - args = Eg004Controller.get_args() - - try: - # 2. Call the worker method to get a list of clickwraps - results = Eg004Controller.worker(args) - except ApiException as err: - return process_error(err) - - # 3. Render the response - return render_template( - "example_done.html", - title="List clickwraps results", - h1="List clickwraps results", - message="Results from the ClickWraps::getClickwraps method:", - json=json.dumps(json.dumps(results.to_dict(), default=str)) - ) - - -@eg004.route("/eg004", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - return render_template( - "eg004_list_clickwraps.html", - title="Getting a list of clickwraps", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename( - path.dirname(__file__)) + "/controller.py", - ) diff --git a/app/click/examples/eg005_clickwrap_responses/controller.py b/app/click/examples/eg005_clickwrap_responses.py similarity index 77% rename from app/click/examples/eg005_clickwrap_responses/controller.py rename to app/click/examples/eg005_clickwrap_responses.py index 3f82bcd2..6809d966 100644 --- a/app/click/examples/eg005_clickwrap_responses/controller.py +++ b/app/click/examples/eg005_clickwrap_responses.py @@ -1,18 +1,17 @@ from docusign_click import AccountsApi from flask import request, session -from ...utils import create_click_api_client +from ..utils import create_click_api_client -class Eg005Controller: +class Eg005ClickwrapResponsesController: @staticmethod def get_args(): """Get required session and request arguments""" return { "account_id": session.get("ds_account_id"), # Represents your {ACCOUNT_ID} "access_token": session.get("ds_access_token"), # Represents your {ACCESS_TOKEN} - "clickwrap_id": session.get("clickwrap_id"), - "client_user_id": request.form.get("client_user_id"), + "clickwrap_id": request.form.get("clickwrap_id"), } @staticmethod @@ -22,17 +21,20 @@ def worker(args): 2. Get clickwrap responses using SDK """ # Step 1. Create an API client with headers + #ds-snippet-start:Click5Step2 api_client = create_click_api_client( access_token=args["access_token"] ) + #ds-snippet-end:Click5Step2 # Step 2. Get clickwrap responses using SDK + #ds-snippet-start:Click5Step3 accounts_api = AccountsApi(api_client) response = accounts_api.get_clickwrap_agreements( account_id=args["account_id"], clickwrap_id=args["clickwrap_id"], - client_user_id=args["client_user_id"], status="agreed" ) + #ds-snippet-end:Click5Step3 return response diff --git a/app/click/examples/eg005_clickwrap_responses/__init__.py b/app/click/examples/eg005_clickwrap_responses/__init__.py deleted file mode 100644 index 45102c7a..00000000 --- a/app/click/examples/eg005_clickwrap_responses/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg005 diff --git a/app/click/examples/eg005_clickwrap_responses/views.py b/app/click/examples/eg005_clickwrap_responses/views.py deleted file mode 100644 index 29e32c7e..00000000 --- a/app/click/examples/eg005_clickwrap_responses/views.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Example 005: Getting clickwrap responses""" - -from os import path -import json - -from docusign_click.client.api_exception import ApiException -from flask import render_template, current_app, Blueprint, session - -from .controller import Eg005Controller -from app.docusign import authenticate -from app.ds_config import DS_CONFIG -from app.error_handlers import process_error - -eg = "eg005" # Reference (and URL) for this example -eg005 = Blueprint("eg005", __name__) - - -@eg005.route("/eg005", methods=["POST"]) -@authenticate(eg=eg) -def clickwrap_responses(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render the response - """ - # 1. Get required arguments - args = Eg005Controller.get_args() - - try: - # 2. Call the worker method to get clickwrap responses - results = Eg005Controller.worker(args) - except ApiException as err: - return process_error(err) - - # 3. Render the response - return render_template( - "example_done.html", - title="Getting clickwrap responses", - h1="Getting clickwrap responses", - message="Results from the ClickWraps::getClickwrapAgreements method:", - json=json.dumps(json.dumps(results.to_dict(), default=str)) - ) - - -@eg005.route("/eg005", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - return render_template( - "eg005_clickwrap_responses.html", - title="Getting clickwrap responses", - clickwrap_ok="clickwrap_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename( - path.dirname(__file__)) + "/controller.py", - ) diff --git a/app/click/examples/eg006_embed_clickwrap.py b/app/click/examples/eg006_embed_clickwrap.py new file mode 100644 index 00000000..acd5a2e7 --- /dev/null +++ b/app/click/examples/eg006_embed_clickwrap.py @@ -0,0 +1,84 @@ +from docusign_click import AccountsApi, UserAgreementRequest +from flask import session, request +import ast + +from ..utils import create_click_api_client + + +class Eg006EmbedClickwrapController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + return { + "account_id": session.get("ds_account_id"), # Represents your {ACCOUNT_ID} + "access_token": session.get("ds_access_token"), # Represents your {ACCESS_TOKEN} + "clickwrap": request.form.get("clickwrap"), + "fullName": request.form.get("fullName"), + "email": request.form.get("email"), + "company": request.form.get("company"), + "title": request.form.get("title"), + "date": request.form.get("date"), + "statuses": ["inactive", "draft"] + } + + @staticmethod + def get_active_clickwraps(args): + """ + 1. Create an API client with hheaders + 2. Get a list of active clickwraps + """ + # Step 1. Create an API client with headers + #ds-snippet-start:Click6Step2 + api_client = create_click_api_client( + access_token=args["access_token"] + ) + #ds-snippet-end:Click6Step2 + + # Step 2. Get a list of active clickwraps + accounts_api = AccountsApi(api_client) + response = accounts_api.get_clickwraps( + account_id=args["account_id"], + status="active" + ) + + return response + + @staticmethod + def worker(args): + """ + 1. Create an API client with headers + 2. Create a clickwrap request model + 3. Update a clickwrap using SDK + """ + # Create an API client with headers + api_client = create_click_api_client( + access_token=args["access_token"] + ) + + # Create a user agreement request model + #ds-snippet-start:Click6Step3 + user_agreement_request = UserAgreementRequest( + client_user_id=args["email"], + document_data={ + "fullName": args["fullName"], + "email": args["email"], + "company": args["company"], + "title": args["title"], + "date": args["date"] + }, + ) + #ds-snippet-end:Click6Step3 + + # Retrieve Agreement URL using SDK + #ds-snippet-start:Click6Step4 + accounts_api = AccountsApi(api_client) + clickwrap = ast.literal_eval(args["clickwrap"]) + print(type(clickwrap)) + response = accounts_api.create_has_agreed( + account_id=args["account_id"], + clickwrap_id=clickwrap["clickwrap_id"], + user_agreement_request=user_agreement_request, + ) + #ds-snippet-end:Click6Step4 + + return response diff --git a/app/click/templates/404.html b/app/click/templates/404.html deleted file mode 100644 index 307da311..00000000 --- a/app/click/templates/404.html +++ /dev/null @@ -1,7 +0,0 @@ - -{% extends "base.html" %} - -{% block content %} -

File Not Found

-

Continue

-{% endblock %} diff --git a/app/click/templates/500.html b/app/click/templates/500.html deleted file mode 100644 index 7caf7e85..00000000 --- a/app/click/templates/500.html +++ /dev/null @@ -1,7 +0,0 @@ - -{% extends "base.html" %} - -{% block content %} -

{{ 'An unexpected error has occurred' }}

-

{{ 'Back' }}

-{% endblock %} diff --git a/app/click/templates/base.html b/app/click/templates/base.html deleted file mode 100644 index 878232d8..00000000 --- a/app/click/templates/base.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - {{ title }} - - - - - - - - - - - - - - - - - -
-
- {% block content %}{% endblock %} -
-
- - - - - - - - - - - - - - - - {% set json_messages = get_flashed_messages() | tojson %} - - - \ No newline at end of file diff --git a/app/click/templates/documentation.html b/app/click/templates/documentation.html deleted file mode 100644 index 4e05ab64..00000000 --- a/app/click/templates/documentation.html +++ /dev/null @@ -1,8 +0,0 @@ -{% block content %} - -{% if show_doc %} -

Documentation - about this example.

-{% endif %} - -{% endblock %} \ No newline at end of file diff --git a/app/click/templates/eg001_create_clickwrap.html b/app/click/templates/eg001_create_clickwrap.html deleted file mode 100644 index cfd9c93d..00000000 --- a/app/click/templates/eg001_create_clickwrap.html +++ /dev/null @@ -1,25 +0,0 @@ - {% extends "base.html" %} {% block content %} -

1. Creating a clickwrap

-

This example demonstrates how to use DocuSign Click to create a - clickwrap that you can embed in your website or app.

- -{% include 'documentation.html' %} - -

API method used: - ClickWraps::createClickwrap -

- -{% include 'github_source.html' %} - -
-
- - -
- - -
- -{% endblock %} diff --git a/app/click/templates/eg002_activate_clickwrap.html b/app/click/templates/eg002_activate_clickwrap.html deleted file mode 100644 index dee6e42a..00000000 --- a/app/click/templates/eg002_activate_clickwrap.html +++ /dev/null @@ -1,34 +0,0 @@ - {% extends "base.html" %} {% block content %} -

2. Activating a clickwrap

-

This example demonstrates how to use DocuSign Click to activate a new - clickwrap that you have already created.
By default, new - clickwraps are inactive. You must activate your clickwrap before - you can use it.

- -{% include 'documentation.html' %} - -

API method used: - ClickWraps::updateClickwrapVersion -

- -{% include 'github_source.html' %} - -{% if clickwrap_ok %} -

The clickwrap you created via example 1 will be queried.

- -
- - -
- -{% else %} -

Problem: please first create a clickwrap using example - 1.
Thank you.

- -
- -
-{% endif %} - -{% endblock %} diff --git a/app/click/templates/eg003_create_new_clickwrap_version.html b/app/click/templates/eg003_create_new_clickwrap_version.html deleted file mode 100644 index 37a92c16..00000000 --- a/app/click/templates/eg003_create_new_clickwrap_version.html +++ /dev/null @@ -1,34 +0,0 @@ - {% extends "base.html" %} {% block content %} -

3. Creating a new clickwrap version

-

This example demonstrates how to use the Click API to create a new version - of a clickwrap.
You can specify whether you require users who have - previously accepted the clickwrap to accept the new version when they - return to your website.

- -{% include 'documentation.html' %} - -

API method used: - ClickWraps::createClickwrapVersion -

- -{% include 'github_source.html' %} - -{% if clickwrap_ok %} -

The clickwrap you created via example 1 will be queried.

- -
- - -
- -{% else %} -

Problem: please first create a clickwrap using example - 1.
Thank you.

- -
- -
-{% endif %} - -{% endblock %} diff --git a/app/click/templates/eg004_list_clickwraps.html b/app/click/templates/eg004_list_clickwraps.html deleted file mode 100644 index 9f38f769..00000000 --- a/app/click/templates/eg004_list_clickwraps.html +++ /dev/null @@ -1,20 +0,0 @@ - {% extends "base.html" %} {% block content %} -

4. Getting a list of clickwraps

-

This example demonstrates how to use the Click API to get a list of - clickwraps associated with a specific DocuSign user.

- -{% include 'documentation.html' %} - -

API method used: - ClickWraps::getClickwraps -

- -{% include 'github_source.html' %} - -
- - -
- -{% endblock %} diff --git a/app/click/templates/eg005_clickwrap_responses.html b/app/click/templates/eg005_clickwrap_responses.html deleted file mode 100644 index 15d56e30..00000000 --- a/app/click/templates/eg005_clickwrap_responses.html +++ /dev/null @@ -1,39 +0,0 @@ - {% extends "base.html" %} {% block content %} -

5. Getting clickwrap responses

-

This example demonstrates how to use the Click API to get a list of - clickwraps associated with a specific DocuSign user.

- -{% include 'documentation.html' %} - -

API method used: - ClickWraps::getClickwrapAgreements -

- -{% include 'github_source.html' %} - -{% if clickwrap_ok %} -

The clickwrap you created via example 1 will be queried.
- Please enter a value that uniquely identifies a user, such as an email - address or employee ID

- -
-
- - -
- - -
- -{% else %} -

Problem: please first create a clickwrap using example - 1.
Thank you.

- -
- -
-{% endif %} - -{% endblock %} diff --git a/app/click/templates/error.html b/app/click/templates/error.html deleted file mode 100644 index 7d8a9913..00000000 --- a/app/click/templates/error.html +++ /dev/null @@ -1,18 +0,0 @@ - -{% extends "base.html" %} -{% set title = "Error" %} -{% block content %} - -

Problem: an error occurred

-

Error information:

- -{% if error_code %} -

{{ error_code }}: {{ error_message }}

-{% else %} -

{{ err }}

-{% endif %} - - -

Continue

- -{% endblock %} diff --git a/app/click/templates/example_done.html b/app/click/templates/example_done.html deleted file mode 100644 index 93a3f2e1..00000000 --- a/app/click/templates/example_done.html +++ /dev/null @@ -1,27 +0,0 @@ - {% extends "base.html" %} {% block content %} - -

{{ h1 }}

-

{{ message | safe }}

-{% if link %} -
- {{ link_text }} -
-{% endif %} - -{% if json %} - - -

-{% endif %} - -{% if changed_settings %} -
    - {% for k, v in changed_settings.items() %} -
  • {{ k }}: {{ v }}
  • - {% endfor %} -
-{% endif %} - -

Continue

- -{% endblock %} diff --git a/app/click/templates/github_source.html b/app/click/templates/github_source.html deleted file mode 100644 index 79391663..00000000 --- a/app/click/templates/github_source.html +++ /dev/null @@ -1,8 +0,0 @@ -{% block content %} - -

- View source file {{ - source_file }} on GitHub. -

- -{% endblock %} \ No newline at end of file diff --git a/app/click/templates/home_click.html b/app/click/templates/home_click.html deleted file mode 100644 index da089034..00000000 --- a/app/click/templates/home_click.html +++ /dev/null @@ -1,82 +0,0 @@ - -{% extends "base.html" %} - -{% block content %} - -
-
- - - - - - - -
-

Python Launcher

-

Welcome to the DocuSign Click Python - examples using multiple OAuth flows (JWT and - Authorization Code Grant).

-
- -
-
- -
-

Welcome

-

This launcher includes the following examples for the DocuSign Click - API.

- - {% if show_doc %} -

Documentation on using JWT - or Authorization Code Grant from a Python Flask application.

- {% endif %} - -

Basic Click API Examples

-

1. Creating a clickwrap

-

This example demonstrates how to use DocuSign Click to create a - clickwrap that you can embed in your website or app.

-

API method used: - ClickWraps::createClickwrap -

-

2. Activating a clickwrap

-

This example demonstrates how to use DocuSign Click to activate a - new clickwrap that you have already created. -

-

API method used: - ClickWraps::updateClickwrapVersion -

-

3. Creating a new clickwrap - version

-

This example demonstrates how to use the Click API to - create a new version of a clickwrap.

-

API method used: - ClickWraps::createClickwrapVersion -

-

4. Getting a list of clickwraps -

-

This example demonstrates how to use the Click API to get a list of - clickwraps associated with a specific DocuSign user.

-

API method used: - ClickWraps::getClickwraps -

-

5. Getting clickwrap responses -

-

This example demonstrates how to use the Click API to get user - responses to your clickwrap agreements.

-

API method used: - ClickWraps::getClickwrapAgreements -

-
- - - - - - {% endblock %} diff --git a/app/click/templates/must_authenticate.html b/app/click/templates/must_authenticate.html deleted file mode 100644 index 88590cd9..00000000 --- a/app/click/templates/must_authenticate.html +++ /dev/null @@ -1,24 +0,0 @@ - -{% extends "base.html" %} - -{% block content %} -
-

Please authenticate with DocuSign

- -
-
- - -

- - -

-
-
-
-

You need to authenticate with DocuSign to continue your request.

-
-{% endblock %} diff --git a/app/click/views/__init__.py b/app/click/views/__init__.py new file mode 100644 index 00000000..2dd59d8d --- /dev/null +++ b/app/click/views/__init__.py @@ -0,0 +1,6 @@ +from .eg001_create_clickwrap import ceg001 +from .eg002_activate_clickwrap import ceg002 +from .eg003_create_new_clickwrap_version import ceg003 +from .eg004_list_clickwraps import ceg004 +from .eg005_clickwrap_responses import ceg005 +from .eg006_embed_clickwrap import ceg006 diff --git a/app/click/examples/eg001_create_clickwrap/views.py b/app/click/views/eg001_create_clickwrap.py similarity index 53% rename from app/click/examples/eg001_create_clickwrap/views.py rename to app/click/views/eg001_create_clickwrap.py index c2a2d610..d184c388 100644 --- a/app/click/examples/eg001_create_clickwrap/views.py +++ b/app/click/views/eg001_create_clickwrap.py @@ -6,33 +6,39 @@ from docusign_click.client.api_exception import ApiException from flask import render_template, current_app, Blueprint, session -from .controller import Eg001Controller -from app.docusign import authenticate +from ..examples.eg001_create_clickwrap import Eg001CreateClickwrapController +from app.docusign import authenticate, ensure_manifest, get_example_by_number from app.ds_config import DS_CONFIG from app.error_handlers import process_error +from ...consts import API_TYPE -eg = "eg001" # Reference (and URL) for this example -eg001 = Blueprint("eg001", __name__) +example_number = 1 +api = API_TYPE["CLICK"] +eg = f"ceg00{example_number}" # Reference (and URL) for this example +ceg001 = Blueprint(eg, __name__) -@eg001.route("/eg001", methods=["POST"]) -@authenticate(eg=eg) +@ceg001.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def create_clickwrap(): """ 1. Get required arguments 2. Call the worker method 3. Render the response """ + example = get_example_by_number(session["manifest"], example_number, api) + # 1. Get required arguments - args = Eg001Controller.get_args() + args = Eg001CreateClickwrapController.get_args() try: # 2. Call the worker method to create a new clickwrap - results = Eg001Controller.worker(args) + results = Eg001CreateClickwrapController.worker(args) clickwrap_id = results.clickwrap_id clickwrap_name = args['clickwrap_name'] current_app.logger.info( - f"""The clickwrap "{clickwrap_name}" has been created!""" + f"""The clickwrap "{clickwrap_name}" has been created.""" ) except ApiException as err: return process_error(err) @@ -45,21 +51,23 @@ def create_clickwrap(): # 3. Render the response return render_template( "example_done.html", - title="Creating a new clickwrap", - h1="Creating a new clickwrap", + title=example["ExampleName"], message=f"""The clickwrap "{args['clickwrap_name']}" has been created!""", json=json.dumps(json.dumps(results.to_dict(), default=str)) ) -@eg001.route("/eg001", methods=["GET"]) -@authenticate(eg=eg) +@ceg001.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def get_view(): """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + return render_template( - "eg001_create_clickwrap.html", - title="Creating a new clickwrap", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename( - path.dirname(__file__)) + "/controller.py", + "click/eg001_create_clickwrap.html", + title=example["ExampleName"], + example=example, + source_file= "eg001_create_clickwrap.py", + source_url=DS_CONFIG["click_github_url"] + "eg001_create_clickwrap.py", ) diff --git a/app/click/views/eg002_activate_clickwrap.py b/app/click/views/eg002_activate_clickwrap.py new file mode 100644 index 00000000..c6796819 --- /dev/null +++ b/app/click/views/eg002_activate_clickwrap.py @@ -0,0 +1,71 @@ +"""Example 002: Activating a clickwrap""" + +from os import path +import json + +from docusign_click.client.api_exception import ApiException +from flask import render_template, current_app, Blueprint, session + +from ..examples.eg002_activate_clickwrap import Eg002ActivateClickwrapController +from app.docusign import authenticate, get_example_by_number, ensure_manifest +from app.ds_config import DS_CONFIG +from app.error_handlers import process_error +from ...consts import API_TYPE + +example_number = 2 +api = API_TYPE["CLICK"] +eg = f"ceg00{example_number}" # Reference (and URL) for this example +ceg002 = Blueprint(eg, __name__) + + +@ceg002.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def activate_clickwrap(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg002ActivateClickwrapController.get_args() + + try: + # 2. Call the worker method to create a new clickwrap + results = Eg002ActivateClickwrapController.worker(args) + current_app.logger.info( + f"""The clickwrap has been activated.""" + ) + except ApiException as err: + return process_error(err) + + # Save for use by other examples which need an clickwrap params. + session["clickwrap_is_active"] = True + + # 3. Render the response + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"""The clickwrap has been activated!""", + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + + +@ceg002.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = Eg002ActivateClickwrapController.get_args() + return render_template( + "click/eg002_activate_clickwrap.html", + title=example["ExampleName"], + example=example, + clickwraps_data=Eg002ActivateClickwrapController.get_inactive_clickwraps(args), + source_file= "eg002_activate_clickwrap.py", + source_url=DS_CONFIG["click_github_url"] + "eg002_activate_clickwrap.py", + ) diff --git a/app/click/views/eg003_create_new_clickwrap_version.py b/app/click/views/eg003_create_new_clickwrap_version.py new file mode 100644 index 00000000..47dd80cb --- /dev/null +++ b/app/click/views/eg003_create_new_clickwrap_version.py @@ -0,0 +1,70 @@ +"""Example 003: Creating a new clickwrap version""" + +from os import path +import json + +from docusign_click.client.api_exception import ApiException +from flask import render_template, current_app, Blueprint, session + +from ..examples.eg003_create_new_clickwrap_version import Eg003CrateNewClickwrapVersionController +from ..examples.eg004_list_clickwraps import Eg004ListClickwrapsController +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.ds_config import DS_CONFIG +from app.error_handlers import process_error +from ...consts import API_TYPE + +example_number = 3 +api = API_TYPE["CLICK"] +eg = f"ceg00{example_number}" # Reference (and URL) for this example +ceg003 = Blueprint(eg, __name__) + + +@ceg003.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def create_new_clickwrap_version(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg003CrateNewClickwrapVersionController.get_args() + + try: + # 2. Call the worker method to create a new clickwrap version + results = Eg003CrateNewClickwrapVersionController.worker(args) + results_dict = results.to_dict() + current_app.logger.info( + f"""Version {results_dict['version_number']} of clickwrap "{results_dict['clickwrap_name']}" has been created.""" + ) + except ApiException as err: + return process_error(err) + + # 3. Render the response + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"""Version {results_dict['version_number']} of clickwrap "{results_dict['clickwrap_name']}" has been created.""", + json=json.dumps(json.dumps(results_dict, default=str)) + ) + + +@ceg003.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = Eg004ListClickwrapsController.get_args() + return render_template( + "click/eg003_create_new_clickwrap_version.html", + title=example["ExampleName"], + example=example, + clickwraps_data=Eg004ListClickwrapsController.worker(args), + source_file= "eg003_create_new_clickwrap_version.py", + source_url=DS_CONFIG["click_github_url"] + "eg003_create_new_clickwrap_version.py", + ) diff --git a/app/click/views/eg004_list_clickwraps.py b/app/click/views/eg004_list_clickwraps.py new file mode 100644 index 00000000..193cafef --- /dev/null +++ b/app/click/views/eg004_list_clickwraps.py @@ -0,0 +1,63 @@ +"""Example 004: Getting a list of clickwraps""" + +from os import path +import json + +from docusign_click.client.api_exception import ApiException +from flask import render_template, current_app, Blueprint, session + +from ..examples.eg004_list_clickwraps import Eg004ListClickwrapsController +from app.docusign import authenticate, get_example_by_number, ensure_manifest +from app.ds_config import DS_CONFIG +from app.error_handlers import process_error +from ...consts import API_TYPE + +example_number = 4 +api = API_TYPE["CLICK"] +eg = f"ceg00{example_number}" # Reference (and URL) for this example +ceg004 = Blueprint(eg, __name__) + + +@ceg004.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def clickwrap_list(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg004ListClickwrapsController.get_args() + + try: + # 2. Call the worker method to get a list of clickwraps + results = Eg004ListClickwrapsController.worker(args) + except ApiException as err: + return process_error(err) + + # 3. Render the response + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from the ClickWraps::getClickwraps method:", + json=json.dumps(json.dumps(results, default=str).replace('\\"', '').replace("'", ""), default=str) + ) + + +@ceg004.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "click/eg004_list_clickwraps.html", + title=example["ExampleName"], + example=example, + source_file= "eg004_list_clickwraps.py", + source_url=DS_CONFIG["click_github_url"] + "eg004_list_clickwraps.py", + ) diff --git a/app/click/views/eg005_clickwrap_responses.py b/app/click/views/eg005_clickwrap_responses.py new file mode 100644 index 00000000..492fc41e --- /dev/null +++ b/app/click/views/eg005_clickwrap_responses.py @@ -0,0 +1,66 @@ +"""Example 005: Getting clickwrap responses""" + +from os import path +import json + +from docusign_click.client.api_exception import ApiException +from flask import render_template, current_app, Blueprint, session + +from ..examples.eg005_clickwrap_responses import Eg005ClickwrapResponsesController +from ..examples.eg004_list_clickwraps import Eg004ListClickwrapsController +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.ds_config import DS_CONFIG +from app.error_handlers import process_error +from ...consts import API_TYPE + +example_number = 5 +api = API_TYPE["CLICK"] +eg = f"ceg00{example_number}" # Reference (and URL) for this example +ceg005 = Blueprint(eg, __name__) + + +@ceg005.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def clickwrap_responses(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg005ClickwrapResponsesController.get_args() + + try: + # 2. Call the worker method to get clickwrap responses + results = Eg005ClickwrapResponsesController.worker(args) + except ApiException as err: + return process_error(err) + + # 3. Render the response + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from the ClickWraps::getClickwrapAgreements method:", + json=json.dumps(json.dumps(results.to_dict(), default=str)) + ) + + +@ceg005.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = Eg004ListClickwrapsController.get_args() + return render_template( + "click/eg005_clickwrap_responses.html", + title=example["ExampleName"], + example=example, + clickwraps_data=Eg004ListClickwrapsController.worker(args), + source_file= "eg005_clickwrap_responses.py", + source_url=DS_CONFIG["click_github_url"] + "eg005_clickwrap_responses.py", + ) diff --git a/app/click/views/eg006_embed_clickwrap.py b/app/click/views/eg006_embed_clickwrap.py new file mode 100644 index 00000000..6d3929e2 --- /dev/null +++ b/app/click/views/eg006_embed_clickwrap.py @@ -0,0 +1,82 @@ +"""Example 006: Embed a clickwrap""" + +from os import path +import json +import webbrowser + +from docusign_click.client.api_exception import ApiException +from flask import render_template, current_app, Blueprint, session + +from ..examples.eg006_embed_clickwrap import Eg006EmbedClickwrapController +from ..examples.eg002_activate_clickwrap import Eg002ActivateClickwrapController +from app.docusign import authenticate, get_example_by_number, ensure_manifest +from app.ds_config import DS_CONFIG +from app.error_handlers import process_error +from ...consts import API_TYPE + +example_number = 6 +api = API_TYPE["CLICK"] +eg = f"ceg00{example_number}" # Reference (and URL) for this example +ceg006 = Blueprint(eg, __name__) + + +@ceg006.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def embed_clickwrap(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg006EmbedClickwrapController.get_args() + + try: + # 2. Call the worker method to create a new clickwrap + results = Eg006EmbedClickwrapController.worker(args) + current_app.logger.info( + f"""See the embedded clickwrap in the dialog box.""" + ) + except ApiException as err: + return process_error(err) + + # Save for use by other examples which need an clickwrap params. + session["clickwrap_is_active"] = True + + if results.to_dict()["agreement_url"] == None: + return render_template( + "error.html", + error_code="200", + error_message="The email address was already used to agree to this elastic template. Provide a different email address if you want to view the agreement and agree to it." + ) + + # 3. Render the response + return render_template( + "click/eg006_done.html", + title=example["ExampleName"], + message=f"""See the embedded clickwrap in the dialog box.""", + json=json.dumps(json.dumps(results.to_dict(), default=str)), + agreementUrl= results.to_dict()["agreement_url"] + ) + + +@ceg006.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = Eg006EmbedClickwrapController.get_args() + return render_template( + "click/eg006_embed_clickwrap.html", + title=example["ExampleName"], + example=example, + clickwraps_data=Eg006EmbedClickwrapController.get_active_clickwraps(args), + inactive_clickwraps_data=Eg002ActivateClickwrapController.get_inactive_clickwraps(args), + source_file= "eg006_embed_clickwrap.py", + source_url=DS_CONFIG["click_github_url"] + "eg006_embed_clickwrap.py", + ) diff --git a/app/connect/__init__.py b/app/connect/__init__.py new file mode 100644 index 00000000..e6e82bd4 --- /dev/null +++ b/app/connect/__init__.py @@ -0,0 +1 @@ +from .views import cneg001 \ No newline at end of file diff --git a/app/connect/examples/eg001_validate_webhook_message.py b/app/connect/examples/eg001_validate_webhook_message.py new file mode 100644 index 00000000..51bc6c53 --- /dev/null +++ b/app/connect/examples/eg001_validate_webhook_message.py @@ -0,0 +1,29 @@ +from flask import request +import hmac +import hashlib +import base64 + +class Eg001ValidateWebhookMessageController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + return { + "secret": request.form.get("secret"), + "payload": request.form.get("payload"), + } + + @staticmethod + def worker(args): + """ + 1. Create an API client with headers + 2. Get your monitor data via SDK + """ + #ds-snippet-start:Connect1Step1 + key = bytes(args['secret'], 'utf-8') + payload = bytes(args['payload'], 'utf-8') + + hmac_hash = hmac.new(key, payload, hashlib.sha256) + result = base64.b64encode(hmac_hash.digest()).decode('utf-8') + #ds-snippet-end:Connect1Step1 + + return result diff --git a/app/connect/views/__init__.py b/app/connect/views/__init__.py new file mode 100644 index 00000000..0e48e2fe --- /dev/null +++ b/app/connect/views/__init__.py @@ -0,0 +1 @@ +from .eg001_validate_webhook_message import cneg001 \ No newline at end of file diff --git a/app/connect/views/eg001_validate_webhook_message.py b/app/connect/views/eg001_validate_webhook_message.py new file mode 100644 index 00000000..e222ec8a --- /dev/null +++ b/app/connect/views/eg001_validate_webhook_message.py @@ -0,0 +1,55 @@ +"""Example 001: Validate webhook message using HMAC. """ + +from docusign_monitor.client.api_exception import ApiException +from flask import Blueprint, render_template, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg001_validate_webhook_message import Eg001ValidateWebhookMessageController +from ...ds_config import DS_CONFIG +from ...consts import API_TYPE + +example_number = 1 +api = API_TYPE["CONNECT"] +eg = f"cneg00{example_number}" # Reference (and URL) for this example +cneg001 = Blueprint(eg, __name__) + +@cneg001.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +def get_monitoring_data(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg001ValidateWebhookMessageController.get_args() + try: + # 2. Call the worker method to compute hash + results = Eg001ValidateWebhookMessageController.worker(args) + except ApiException as err: + return process_error(err) + + return render_template( + "example_done.html", + title=example["ExampleName"], + message=example["ResultsPageText"].format(results) + ) + +@cneg001.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "connect/eg001_validate_webhook_message.html", + title=example["ExampleName"], + example=example, + source_file= "eg001_validate_webhook_message.py", + source_url=DS_CONFIG["connect_github_url"] + "eg001_validate_webhook_message.py", + documentation=DS_CONFIG["documentation"] + eg, + ) + diff --git a/app/connected_fields/examples/eg001_set_connected_fields.py b/app/connected_fields/examples/eg001_set_connected_fields.py new file mode 100644 index 00000000..ece80005 --- /dev/null +++ b/app/connected_fields/examples/eg001_set_connected_fields.py @@ -0,0 +1,235 @@ +import base64 +import requests +from os import path + +from docusign_esign import EnvelopesApi, Text, Document, Signer, EnvelopeDefinition, SignHere, Tabs, \ + Recipients +from flask import session, request + +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG + + +class Eg001SetConnectedFieldsController: + @staticmethod + def get_args(): + """Get request and session arguments""" + # Parse request arguments + signer_email = pattern.sub("", request.form.get("signer_email")) + signer_name = pattern.sub("", request.form.get("signer_name")) + selected_app_id = pattern.sub("", request.form.get("app_id")) + envelope_args = { + "signer_email": signer_email, + "signer_name": signer_name, + } + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "selected_app_id": selected_app_id, + "envelope_args": envelope_args + } + return args + + @staticmethod + def get_tab_groups(args): + """ + 1. Get the list of tab groups + 2. Filter by action contract and tab label + 3. Create a list of unique apps + """ + + #ds-snippet-start:ConnectedFields1Step2 + headers = { + "Authorization": "Bearer " + args['access_token'], + "Accept": "application/json", + "Content-Type": "application/json" + } + #ds-snippet-end:ConnectedFields1Step2 + + #ds-snippet-start:ConnectedFields1Step3 + url = f"{args['base_path']}/v1/accounts/{args['account_id']}/connected-fields/tab-groups" + + response = requests.get(url, headers=headers) + response_data = response.json() + + filtered_apps = list( + app for app in response_data + if any( + ("extensionData" in tab and "actionContract" in tab["extensionData"] and "Verify" in tab["extensionData"]["actionContract"]) or + ("tabLabel" in tab and "connecteddata" in tab["tabLabel"]) + for tab in app.get("tabs", []) + ) + ) + + unique_apps = list({app['appId']: app for app in filtered_apps}.values()) + #ds-snippet-end:ConnectedFields1Step3 + + return unique_apps + + @staticmethod + #ds-snippet-start:ConnectedFields1Step4 + def extract_verification_data(selected_app_id, tab): + extension_data = tab["extensionData"] + + return { + "app_id": selected_app_id, + "extension_group_id": extension_data["extensionGroupId"] if "extensionGroupId" in extension_data else "", + "publisher_name": extension_data["publisherName"] if "publisherName" in extension_data else "", + "application_name": extension_data["applicationName"] if "applicationName" in extension_data else "", + "action_name": extension_data["actionName"] if "actionName" in extension_data else "", + "action_input_key": extension_data["actionInputKey"] if "actionInputKey" in extension_data else "", + "action_contract": extension_data["actionContract"] if "actionContract" in extension_data else "", + "extension_name": extension_data["extensionName"] if "extensionName" in extension_data else "", + "extension_contract": extension_data["extensionContract"] if "extensionContract" in extension_data else "", + "required_for_extension": extension_data["requiredForExtension"] if "requiredForExtension" in extension_data else "", + "tab_label": tab["tabLabel"], + "connection_key": ( + extension_data["connectionInstances"][0]["connectionKey"] + if "connectionInstances" in extension_data and extension_data["connectionInstances"] + else "" + ), + "connection_value": ( + extension_data["connectionInstances"][0]["connectionValue"] + if "connectionInstances" in extension_data and extension_data["connectionInstances"] + else "" + ), + } + #ds-snippet-end:ConnectedFields1Step4 + + @classmethod + def send_envelope(cls, args, app): + """ + 1. Create the envelope request object + 2. Send the envelope + 3. Obtain the envelope_id + """ + #ds-snippet-start:ConnectedFields1Step6 + envelope_args = args["envelope_args"] + # Create the envelope request object + envelope_definition = cls.make_envelope(envelope_args, app) + + # Call Envelopes::create API method + # Exceptions will be caught by the calling function + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + + envelope_api = EnvelopesApi(api_client) + results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + + envelope_id = results.envelope_id + #ds-snippet-end:ConnectedFields1Step6 + + return {"envelope_id": envelope_id} + + @classmethod + #ds-snippet-start:ConnectedFields1Step5 + def make_envelope(cls, args, app): + """ + Creates envelope + args -- parameters for the envelope: + signer_email, signer_name + returns an envelope definition + """ + + # document 1 (pdf) has tag /sn1/ + # + # The envelope has one recipient. + # recipient 1 - signer + with open(path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), "rb") as file: + content_bytes = file.read() + base64_file_content = base64.b64encode(content_bytes).decode("ascii") + + # Create the document model + document = Document( # create the DocuSign document object + document_base64=base64_file_content, + name="Example document", # can be different from actual file name + file_extension="pdf", # many different document types are accepted + document_id=1 # a label used to reference the doc + ) + + # Create the signer recipient model + signer = Signer( + # The signer + email=args["signer_email"], + name=args["signer_name"], + recipient_id="1", + routing_order="1" + ) + + # Create a sign_here tab (field on the document) + sign_here = SignHere( + anchor_string="/sn1/", + anchor_units="pixels", + anchor_y_offset="10", + anchor_x_offset="20" + ) + + # Create text tabs (field on the document) + text_tabs = [] + for tab in (t for t in app["tabs"] if "SuggestionInput" not in t["tabLabel"]): + verification_data = cls.extract_verification_data(app["appId"], tab) + extension_data = cls.get_extension_data(verification_data) + + text_tab = { + "requireInitialOnSharedChange": False, + "requireAll": False, + "name": verification_data["application_name"], + "required": True, + "locked": False, + "disableAutoSize": False, + "maxLength": 4000, + "tabLabel": verification_data["tab_label"], + "font": "lucidaconsole", + "fontColor": "black", + "fontSize": "size9", + "documentId": "1", + "recipientId": "1", + "pageNumber": "1", + "xPosition": f"{70 + 100 * int(len(text_tabs) / 10)}", + "yPosition": f"{560 + 20 * (len(text_tabs) % 10)}", + "width": "84", + "height": "22", + "templateRequired": False, + "tabType": "text", + "tooltip": verification_data["action_input_key"], + "extensionData": extension_data + } + text_tabs.append(text_tab) + + # Add the tabs model (including the sign_here and text tabs) to the signer + # The Tabs object wants arrays of the different field/tab types + signer.tabs = Tabs(sign_here_tabs=[sign_here], text_tabs=text_tabs) + + # Next, create the top level envelope definition and populate it. + envelope_definition = EnvelopeDefinition( + email_subject="Please sign this document", + documents=[document], + # The Recipients object wants arrays for each recipient type + recipients=Recipients(signers=[signer]), + status="sent" # requests that the envelope be created and sent. + ) + + return envelope_definition + + def get_extension_data(verification_data): + return { + "extensionGroupId": verification_data["extension_group_id"], + "publisherName": verification_data["publisher_name"], + "applicationId": verification_data["app_id"], + "applicationName": verification_data["application_name"], + "actionName": verification_data["action_name"], + "actionContract": verification_data["action_contract"], + "extensionName": verification_data["extension_name"], + "extensionContract": verification_data["extension_contract"], + "requiredForExtension": verification_data["required_for_extension"], + "actionInputKey": verification_data["action_input_key"], + "extensionPolicy": 'MustVerifyToSign', + "connectionInstances": [ + { + "connectionKey": verification_data["connection_key"], + "connectionValue": verification_data["connection_value"], + } + ] + } + #ds-snippet-end:ConnectedFields1Step5 diff --git a/app/connected_fields/views/__init__.py b/app/connected_fields/views/__init__.py new file mode 100644 index 00000000..c0ffe6d2 --- /dev/null +++ b/app/connected_fields/views/__init__.py @@ -0,0 +1 @@ +from .eg001_set_connected_fields import feg001 \ No newline at end of file diff --git a/app/connected_fields/views/eg001_set_connected_fields.py b/app/connected_fields/views/eg001_set_connected_fields.py new file mode 100644 index 00000000..c4fe8ab5 --- /dev/null +++ b/app/connected_fields/views/eg001_set_connected_fields.py @@ -0,0 +1,83 @@ +"""Example 001: Set connected fields""" + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, redirect, Blueprint, session + +from ..examples.eg001_set_connected_fields import Eg001SetConnectedFieldsController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 1 +api = API_TYPE["CONNECTED_FIELDS"] +eg = f"feg00{example_number}" # reference (and url) for this example +feg001 = Blueprint(eg, __name__) + + +@feg001.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def set_connected_fields(): + """ + 1. Get required arguments + 2. Call the worker method + """ + try: + # 1. Get required arguments + args = Eg001SetConnectedFieldsController.get_args() + # 2. Call the worker method + selected_app = next((app for app in session["apps"] if app["appId"] == args["selected_app_id"]), None) + results = Eg001SetConnectedFieldsController.send_envelope(args, selected_app) + except ApiException as err: + return process_error(err) + + session["envelope_id"] = results["envelope_id"] + + example = get_example_by_number(session["manifest"], example_number, api) + return render_template( + "example_done.html", + title=example["ExampleName"], + message=example["ResultsPageText"].format(results['envelope_id']) + ) + + +@feg001.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = { + "account_id": session["ds_account_id"], + "base_path": "https://api-d.docusign.com", + "access_token": session["ds_access_token"], + } + apps = Eg001SetConnectedFieldsController.get_tab_groups(args) + + if not apps or len(apps) == 0: + additional_page_data = next( + (p for p in example["AdditionalPage"] if p["Name"] == "no_verification_app"), + None + ) + + return render_template( + "example_done.html", + title=example["ExampleName"], + message=additional_page_data["ResultsPageText"] + ) + + session["apps"] = apps + return render_template( + "connected_fields/eg001_set_connected_fields.html", + title=example["ExampleName"], + example=example, + apps=apps, + source_file="eg001_set_connected_fields.py", + source_url=DS_CONFIG["github_example_url"] + "eg001_set_connected_fields.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/consts.py b/app/consts.py index 03b6886a..bf24e8e8 100644 --- a/app/consts.py +++ b/app/consts.py @@ -15,16 +15,22 @@ minimum_buffer_min = 3 # Template name for create template example -template_name = "Example Signer and CC template" +template_name = "Example Signer and CC template v2" # Name of static doc file doc_file = "World_Wide_Corp_fields.pdf" # Name of static pdf file pdf_file = "World_Wide_Corp_lorem.pdf" +web_form_template_file = "World_Wide_Corp_Web_Form.pdf" + +web_form_config_file = "web-form-config.json" + # Base uri for callback function base_uri_suffix = "/restapi" +# Workflow name +workflow_name = "Example workflow - send invite to signer" # Default languages for brand languages = { @@ -103,3 +109,14 @@ "vaultingMode": "none" } +API_TYPE = { + "ESIGNATURE": "eSignature", + "MONITOR": "Monitor", + "CLICK": "Click", + "ROOMS": "Rooms", + "ADMIN": "Admin", + "CONNECT": "Connect", + "WEBFORMS": "WebForms", + "NOTARY": "Notary", + "CONNECTED_FIELDS": "ConnectedFields" +} diff --git a/app/docusign/__init__.py b/app/docusign/__init__.py index 6898cf89..dc0a68ba 100644 --- a/app/docusign/__init__.py +++ b/app/docusign/__init__.py @@ -1,2 +1,3 @@ from .ds_client import DSClient -from .utils import ds_token_ok, create_api_client, authenticate +from .utils import ds_token_ok, create_api_client, authenticate, ensure_manifest, get_example_by_number, get_manifest, \ + get_user_info diff --git a/app/docusign/ds_client.py b/app/docusign/ds_client.py index 8d1b9bf1..a8408622 100644 --- a/app/docusign/ds_client.py +++ b/app/docusign/ds_client.py @@ -1,18 +1,24 @@ import uuid -from os import path +from os import path, urandom +import hashlib +import base64 +import secrets import requests -from flask import current_app as app, url_for, redirect, render_template, request +from flask import current_app as app, url_for, redirect, render_template, request, session, redirect from flask_oauthlib.client import OAuth +from requests_oauthlib import OAuth2Session from docusign_esign import ApiClient from docusign_esign.client.api_exception import ApiException -from ..ds_config import DS_CONFIG, DS_JWT, EXAMPLES_API_TYPE +from ..ds_config import DS_CONFIG, DS_JWT from ..error_handlers import process_error +from ..jwt_helpers import get_jwt_token, get_private_key +from ..consts import API_TYPE SCOPES = [ - "signature" + "signature" ] ROOMS_SCOPES = [ @@ -25,27 +31,63 @@ "signature", "click.manage", "click.send" ] +ADMIN_SCOPES = [ + "signature", "organization_read", "group_read", "permission_read", "user_read", "user_write", + "account_read", "domain_read", "identity_provider_read", "impersonation", "user_data_redact", + "asset_group_account_read", "asset_group_account_clone_write", "asset_group_account_clone_read", + "organization_sub_account_write", "organization_sub_account_read" +] + +WEBFORMS_SCOPES = [ + "signature", "webforms_read", "webforms_instance_read", "webforms_instance_write" +] + +NOTARY_SCOPES = [ + "signature", "organization_read", "notary_read", "notary_write" +] + +CONNECTED_FIELDS = [ + "signature", "adm_store_unified_repo_read" +] + class DSClient: ds_app = None @classmethod - def _init(cls, auth_type): + def _init(cls, auth_type, api): if auth_type == "code_grant": - cls._auth_code_grant() + if session.get("pkce_failed", False): + cls._auth_code_grant(api) + else: + cls._pkce_auth(api) elif auth_type == "jwt": - cls._jwt_auth() + cls._jwt_auth(api) @classmethod - def _auth_code_grant(cls): + def _auth_code_grant(cls, api): """Authorize with the Authorization Code Grant - OAuth 2.0 flow""" oauth = OAuth(app) - if EXAMPLES_API_TYPE["Rooms"]: - use_scopes = ROOMS_SCOPES - elif EXAMPLES_API_TYPE["Click"]: - use_scopes = CLICK_SCOPES + + use_scopes = [] + + if api == "Rooms": + use_scopes.extend(ROOMS_SCOPES) + elif api == "Click": + use_scopes.extend(CLICK_SCOPES) + elif api == "Admin": + use_scopes.extend(ADMIN_SCOPES) + elif api == "WebForms": + use_scopes.extend(WEBFORMS_SCOPES) + elif api == "Notary": + use_scopes.extend(NOTARY_SCOPES) + elif api == "ConnectedFields": + use_scopes.extend(CONNECTED_FIELDS) else: - use_scopes = SCOPES + use_scopes.extend(SCOPES) + # remove duplicate scopes + use_scopes = list(set(use_scopes)) + request_token_params = { "scope": " ".join(use_scopes), "state": lambda: uuid.uuid4().hex.upper() @@ -65,23 +107,57 @@ def _auth_code_grant(cls): ) @classmethod - def _jwt_auth(cls): + def _pkce_auth(cls, api): + """Authorize with the Authorization Code Grant - OAuth 2.0 flow""" + use_scopes = [] + + if api == "Rooms": + use_scopes.extend(ROOMS_SCOPES) + elif api == "Click": + use_scopes.extend(CLICK_SCOPES) + elif api == "Admin": + use_scopes.extend(ADMIN_SCOPES) + elif api == "WebForms": + use_scopes.extend(WEBFORMS_SCOPES) + elif api == "Notary": + use_scopes.extend(NOTARY_SCOPES) + elif api == "ConnectedFields": + use_scopes.extend(CONNECTED_FIELDS) + else: + use_scopes.extend(SCOPES) + # remove duplicate scopes + use_scopes = list(set(use_scopes)) + + redirect_uri = DS_CONFIG["app_url"] + url_for("ds.ds_callback") + cls.ds_app = OAuth2Session(DS_CONFIG["ds_client_id"], redirect_uri=redirect_uri, scope=use_scopes) + + @classmethod + def _jwt_auth(cls, api): """JSON Web Token authorization""" api_client = ApiClient() api_client.set_base_path(DS_JWT["authorization_server"]) - if EXAMPLES_API_TYPE["Rooms"]: - use_scopes = ROOMS_SCOPES - elif EXAMPLES_API_TYPE["Click"]: - use_scopes = CLICK_SCOPES + use_scopes = [] + if api == "Rooms": + use_scopes.extend(ROOMS_SCOPES) + elif api == "Click": + use_scopes.extend(CLICK_SCOPES) + elif api == "Admin": + use_scopes.extend(ADMIN_SCOPES) + elif api == "WebForms": + use_scopes.extend(WEBFORMS_SCOPES) + elif api == "Notary": + use_scopes.extend(NOTARY_SCOPES) else: - use_scopes = SCOPES + use_scopes.extend(SCOPES) + # remove duplicate scopes + use_scopes = list(set(use_scopes)) use_scopes.append("impersonation") # Catch IO error try: - private_key = cls._get_private_key().encode("ascii").decode("utf-8") + private_key = get_private_key(DS_JWT["private_key_file"]).encode("ascii").decode("utf-8") except (OSError, IOError) as err: return render_template( "error.html", @@ -89,21 +165,13 @@ def _jwt_auth(cls): ) try: - cls.ds_app = api_client.request_jwt_user_token( - client_id=DS_JWT["ds_client_id"], - user_id=DS_JWT["ds_impersonated_user_id"], - oauth_host_name=DS_JWT["authorization_server"], - private_key_bytes=private_key, - expires_in=3600, - scopes=use_scopes - ) - + cls.ds_app = get_jwt_token(private_key, use_scopes, DS_JWT["authorization_server"], DS_JWT["ds_client_id"], DS_JWT["ds_impersonated_user_id"]) return redirect(url_for("ds.ds_callback")) except ApiException as err: body = err.body.decode('utf8') - # Grand explicit consent for the application + # Grant explicit consent for the application if "consent_required" in body: consent_scopes = " ".join(use_scopes) redirect_uri = DS_CONFIG["app_url"] + url_for("ds.ds_callback") @@ -113,38 +181,34 @@ def _jwt_auth(cls): else: process_error(err) + return redirect(url_for("ds.ds_callback")) + @classmethod def destroy(cls): cls.ds_app = None - @staticmethod - def _get_private_key(): - """ - Check that the private key present in the file and if it is, get it from the file. - In the opposite way get it from config variable. - """ - private_key_file = path.abspath(DS_JWT["private_key_file"]) - - if path.isfile(private_key_file): - with open(private_key_file) as private_key_file: - private_key = private_key_file.read() - else: - private_key = DS_JWT["private_key_file"] - - return private_key - @classmethod - def login(cls, auth_type): + def login(cls, auth_type, api): + cls._init(auth_type, api) if auth_type == "code_grant": - return cls.get(auth_type).authorize(callback=url_for("ds.ds_callback", _external=True)) + if session.get("pkce_failed", False): + return cls.get(auth_type, api).authorize(callback=url_for("ds.ds_callback", _external=True)) + else: + code_verifier = cls.generate_code_verifier() + code_challenge = cls.generate_code_challenge(code_verifier) + session["code_verifier"] = code_verifier + return redirect(cls.get_auth_url_with_pkce(code_challenge)) elif auth_type == "jwt": - return cls._jwt_auth() + return cls._jwt_auth(api) @classmethod def get_token(cls, auth_type): resp = None if auth_type == "code_grant": - resp = cls.get(auth_type).authorized_response() + if session.get("pkce_failed", False): + resp = cls.get(auth_type).authorized_response() + else: + return cls.fetch_token_with_pkce(request.url) elif auth_type == "jwt": resp = cls.get(auth_type).to_dict() @@ -169,7 +233,48 @@ def get_user(cls, access_token): return response @classmethod - def get(cls, auth_type): + def get(cls, auth_type, api=API_TYPE["ESIGNATURE"]): if not cls.ds_app: - cls._init(auth_type) + cls._init(auth_type, api) return cls.ds_app + + @classmethod + def generate_code_verifier(cls): + # Generate a random 32-byte string and base64-url encode it + return secrets.token_urlsafe(32) + + @classmethod + def generate_code_challenge(cls, code_verifier): + # Hash the code verifier using SHA-256 + sha256_hash = hashlib.sha256(code_verifier.encode()).digest() + + # Base64 encode the hash and make it URL safe + base64_encoded = base64.urlsafe_b64encode(sha256_hash).decode().rstrip('=') + + return base64_encoded + + @classmethod + def get_auth_url_with_pkce(cls, code_challenge): + authorize_url = DS_CONFIG["authorization_server"] + "/oauth/auth" + auth_url, state = cls.ds_app.authorization_url( + authorize_url, + code_challenge=code_challenge, + code_challenge_method='S256', # PKCE uses SHA-256 hashing, + approval_prompt="auto" + ) + + return auth_url + + @classmethod + def fetch_token_with_pkce(cls, authorization_response): + access_token_url = DS_CONFIG["authorization_server"] + "/oauth/token" + token = cls.get("code_grant", session.get("api")).fetch_token( + access_token_url, + authorization_response=authorization_response, + client_id=DS_CONFIG["ds_client_id"], + client_secret=DS_CONFIG["ds_client_secret"], + code_verifier=session["code_verifier"], + code_challenge_method="S256" + ) + + return token diff --git a/app/docusign/utils.py b/app/docusign/utils.py index 934995af..eefc0c04 100644 --- a/app/docusign/utils.py +++ b/app/docusign/utils.py @@ -1,11 +1,17 @@ from datetime import timedelta, datetime from functools import wraps +import requests +from urllib.parse import urlparse, parse_qs +import json +import re -from docusign_esign import ApiClient -from flask import session, flash, url_for, redirect +from docusign_esign import ApiClient, AccountsApi +from flask import session, flash, url_for, redirect, render_template, current_app +from jinja2 import environment from .ds_client import DSClient -from ..consts import minimum_buffer_min +from ..consts import minimum_buffer_min, API_TYPE +from ..error_handlers import process_error def ds_logout_internal(): @@ -23,6 +29,7 @@ def ds_logout_internal(): session.pop("envelope_documents", None) session.pop("template_id", None) session.pop("auth_type", None) + session.pop("api", None) DSClient.destroy() @@ -46,11 +53,36 @@ def ds_token_ok(buffer_min=60): return ok +def get_manifest(manifest_url): + try: + manifest = requests.get(manifest_url).json() + return manifest + except Exception as e: + current_app.logger.info(f"Could not load code examples manifest. Manifest URL: {manifest_url} with error {str(e)}") + raise Exception(f"Could not load code examples manifest. Manifest URL: {manifest_url} with error {str(e)}") -def authenticate(eg): +def get_example_by_number(manifest, number, apiName): + for api in manifest["APIs"]: + if api["Name"] == apiName: + for group in api["Groups"]: + for example in group["Examples"]: + if example["ExampleNumber"] == number: + return example + +def authenticate(eg, api): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): + if not (session.get("api") == api or (api == API_TYPE["ESIGNATURE"] and not session.get("api"))): + session["api"] = api + session["eg"] = url_for(eg + ".get_view") + + if api == API_TYPE["MONITOR"]: + session["auth_type"] = "jwt" + return redirect(url_for("ds.ds_login")) + else: + return redirect(url_for("ds.ds_must_authenticate")) + if ds_token_ok(minimum_buffer_min): return func(*args, **kwargs) else: @@ -69,3 +101,68 @@ def wrapper(*args, **kwargs): return wrapper return decorator + +def authenticate_agent(eg): + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + session["eg"] = url_for(eg + ".list_envelopes") + + if ds_token_ok(minimum_buffer_min): + return func(*args, **kwargs) + else: + return redirect(url_for("ds.ds_must_authenticate")) + + return wrapper + + return decorator + +def ensure_manifest(manifest_url): + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + manifest = get_manifest(manifest_url=manifest_url) + session["manifest"] = manifest + + return func(*args, **kwargs) + + return wrapper + + return decorator + +def to_json(value): + return json.dumps(value) + +environment.DEFAULT_FILTERS['to_json'] = to_json + +def is_cfr(accessToken, accountId, basePath): + + api_client = create_api_client(basePath, accessToken) + accounts_api = AccountsApi(api_client) + account_details = accounts_api.get_account_information(accountId) + + return account_details.status21_cfr_part11 + +def get_user_info(access_token, base_path, oauth_host_name): + api_client = create_api_client(base_path, access_token) + api_client.set_oauth_host_name(oauth_host_name) + return api_client.get_user_info(access_token) + +def get_parameter_value_from_url(url, param_name): + parsed_url = urlparse(url) + query_params = parse_qs(parsed_url.query) + + # Access the parameter value (returns a list) + param_value_list = query_params.get(param_name, []) + + # If the parameter exists, return the first value; otherwise, return None + return param_value_list[0] if param_value_list else None + +def replace_template_id(file_path, template_id): + with open(file_path, 'r') as file: + content = file.read() + + content = re.sub('template-id', template_id, content) + + with open(file_path, 'w') as file: + file.write(content) diff --git a/app/docusign/views.py b/app/docusign/views.py index fd2db6e5..d056de50 100644 --- a/app/docusign/views.py +++ b/app/docusign/views.py @@ -2,22 +2,29 @@ from flask import redirect, request, url_for, flash, render_template, Blueprint, session, current_app as app +import json from .ds_client import DSClient -from .utils import ds_logout_internal +from .utils import ds_logout_internal, get_manifest, is_cfr from ..consts import base_uri_suffix from ..ds_config import DS_CONFIG -from ..ds_config import EXAMPLES_API_TYPE +from ..api_type import EXAMPLES_API_TYPE ds = Blueprint("ds", __name__, url_prefix="/ds") - +manifest_url = DS_CONFIG["example_manifest_url"] @ds.route("/login", methods=["GET", "POST"]) def ds_login(): + if session.get('manifest'): + session.pop('manifest') + if not session.get("auth_type"): session["auth_type"] = request.form.get("auth_type") + + session["manifest"] = get_manifest(manifest_url) + app.config["isLoggedIn"] = True app.config["quickstart"] = DS_CONFIG["quickstart"] - return DSClient.login(session["auth_type"]) + return DSClient.login(session["auth_type"], session.get("api")) @ds.route("/logout") @@ -26,8 +33,49 @@ def ds_logout(): flash("You have logged out from DocuSign.") app.config["isLoggedIn"] = False app.config["quickstart"] = False + return redirect(url_for("core.index")) +@ds.route("/choose_api") +def choose_api(): + session["manifest"] = get_manifest(manifest_url) + return render_template("choose_api.html", title="Choose API", manifest=session["manifest"]) + +@ds.route("/api_selected", methods=["GET", "POST"]) +def api_selected(): + chosen_api = request.form.get("chosen_api") + + new_api_type = EXAMPLES_API_TYPE + + # Set all values to False + for api_type in new_api_type: + if new_api_type[api_type] == True: + new_api_type[api_type] = False + + # Update the new chosen API type to True + if chosen_api == "ESignature": + new_api_type["ESignature"] = True + elif chosen_api == "Rooms": + new_api_type["Rooms"] = True + elif chosen_api == "Admin": + new_api_type["Admin"] = True + elif chosen_api == "Monitor": + new_api_type["Monitor"] = True + elif chosen_api == "Click": + new_api_type["Click"] = True + + # Overwrite api_type.py file + with open("app/api_type.py", "w") as api_type_file: + api_type_file.write("EXAMPLES_API_TYPE =" + str(new_api_type)) + + ds_logout_internal() + flash("You have logged out from DocuSign.") + app.config["isLoggedIn"] = False + app.config["quickstart"] = False + + session["manifest"] = get_manifest(manifest_url) + return render_template("must_authenticate.html", title="Must authenticate", chosen_api=chosen_api, manifest=session["manifest"]) + @ds.route("/callback") def ds_callback(): @@ -38,7 +86,14 @@ def ds_callback(): # Save the redirect eg if present redirect_url = session.pop("eg", None) - resp = DSClient.get_token(session["auth_type"]) + try: + resp = DSClient.get_token(session["auth_type"]) + except Exception as err: + if session.get("pkce_failed", False): + raise err + + session["pkce_failed"] = True + return redirect(url_for("ds.ds_login")) # app.logger.info("Authenticated with DocuSign.") session["ds_access_token"] = resp["access_token"] @@ -70,6 +125,9 @@ def ds_callback(): session["ds_account_name"] = account["account_name"] session["ds_base_path"] = account["base_uri"] + base_uri_suffix + if EXAMPLES_API_TYPE["ESignature"] == "true": + session["is_cfr"] = is_cfr(session["ds_access_token"], session["ds_account_id"], session["ds_base_path"]) + if not redirect_url: redirect_url = url_for("core.index") return redirect(redirect_url) @@ -77,7 +135,7 @@ def ds_callback(): @ds.route("/must_authenticate") def ds_must_authenticate(): - if DS_CONFIG["quickstart"] == "true" and EXAMPLES_API_TYPE['ESignature']: + if DS_CONFIG["quickstart"] == "true" and EXAMPLES_API_TYPE["ESignature"]: session["auth_type"] = "code_grant" return redirect(url_for("ds.ds_login")) @@ -86,7 +144,8 @@ def ds_must_authenticate(): return redirect(url_for("ds.ds_login")) else: - return render_template("must_authenticate.html", title="Must authenticate") + session["manifest"] = get_manifest(manifest_url) + return render_template("must_authenticate.html", title="Must authenticate", manifest=session["manifest"]) @ds.route("/ds_return") @@ -95,10 +154,13 @@ def ds_return(): state = request.args.get("state") envelope_id = request.args.get("envelopeId") DS_CONFIG["quickstart"] = "false" + + session["manifest"] = get_manifest(manifest_url) return render_template( "ds_return.html", title="Return from DocuSign", event=event, envelope_id=envelope_id, - state=state - ) + state=state, + manifest=session["manifest"] + ) \ No newline at end of file diff --git a/app/ds_config_sample.py b/app/ds_config_sample.py index ad95522d..71899332 100644 --- a/app/ds_config_sample.py +++ b/app/ds_config_sample.py @@ -1,22 +1,25 @@ # ds_config.py # -# DocuSign configuration settings +# Docusign configuration settings DS_CONFIG = { - "ds_client_id": "{INTEGRATION_KEY_AUTH_CODE}", # The app's DocuSign integration key - "ds_client_secret": "{SECRET_KEY}", # The app's DocuSign integration key's secret + "ds_client_id": "{INTEGRATION_KEY_AUTH_CODE}", # The app's Docusign integration key + "ds_client_secret": "{SECRET_KEY}", # The app's Docusign integration key's secret + "organization_id": "{ORGANIZATION_ID}", # A GUID value that identifies the organization "signer_email": "{SIGNER_EMAIL}", "signer_name": "{SIGNER_NAME}", - "app_url": "http://localhost:5000", # The URL of the application. Eg http://localhost:5000 + "app_url": "http://localhost:3000", # The URL of the application. Eg http://localhost:5000 # NOTE: You must add a Redirect URI of appUrl/ds/callback to your Integration Key. # Example: http://localhost:5000/ds/callback "authorization_server": "https://account-d.docusign.com", "click_api_client_host": "https://demo.docusign.net/clickapi", "rooms_api_client_host": "https://demo.rooms.docusign.com/restapi", "monitor_api_client_host": "https://lens-d.docusign.net", + "admin_api_client_host": "https://api-d.docusign.net/management", + "webforms_api_client_host": "https://apps-d.docusign.com/api/webforms", "allow_silent_authentication": True, # a user can be silently authenticated if they have an # active login session on another tab of the same browser - "target_account_id": None, # Set if you want a specific DocuSign AccountId, + "target_account_id": None, # Set if you want a specific Docusign AccountId, # If None, the user's default account will be used. "demo_doc_path": "demo_documents", "doc_salary_docx": "World_Wide_Corp_salary.docx", @@ -24,11 +27,18 @@ "doc_pdf": "World_Wide_Corp_lorem.pdf", "doc_terms_pdf": "Term_Of_Service.pdf", "doc_txt": "Welcome.txt", + "doc_offer_letter": "Offer_Letter_Demo.docx", + "doc_dynamic_table": "Offer_Letter_Dynamic_Table.docx", # Payment gateway information is optional "gateway_account_id": "{DS_PAYMENT_GATEWAY_ID}", "gateway_name": "stripe", "gateway_display_name": "Stripe", "github_example_url": "https://github.com/docusign/code-examples-python/tree/master/app/eSignature/examples/", + "monitor_github_url": "https://github.com/docusign/code-examples-python/tree/master/app/monitor/examples/", + "admin_github_url": "https://github.com/docusign/code-examples-python/tree/master/app/admin/examples/", + "click_github_url": "https://github.com/docusign/code-examples-python/tree/master/app/click/examples/", + "connect_github_url": "https://github.com/docusign/code-examples-python/tree/master/app/connect/examples/", + "example_manifest_url": "https://raw.githubusercontent.com/docusign/code-examples-csharp/master/manifest/CodeExamplesManifest.json", "documentation": "", # Use an empty string to indicate no documentation path. "quickstart": "{QUICKSTART_VALUE}" } @@ -39,10 +49,3 @@ "private_key_file": "./app/private.key", # Create a new file in your repo source folder named private.key then copy and paste your RSA private key there and save it. "authorization_server": "account-d.docusign.com" } - -EXAMPLES_API_TYPE = { - "Rooms": False, - "ESignature": True, - "Click": False, - "Monitor": False, -} diff --git a/app/eg001_embedded_signing/controller.py b/app/eSignature/examples/eg001_embedded_signing.py similarity index 90% rename from app/eg001_embedded_signing/controller.py rename to app/eSignature/examples/eg001_embedded_signing.py index aeb91359..474f7441 100644 --- a/app/eg001_embedded_signing/controller.py +++ b/app/eSignature/examples/eg001_embedded_signing.py @@ -5,12 +5,12 @@ Recipients from flask import session, url_for, request -from ..consts import authentication_method, demo_docs_path, pattern, signer_client_id -from ..docusign import create_api_client -from ..ds_config import DS_CONFIG +from ...consts import authentication_method, demo_docs_path, pattern, signer_client_id +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG -class Eg001Controller: +class Eg001EmbeddedSigningController: @staticmethod def get_args(): """Get request and session arguments""" @@ -41,6 +41,7 @@ def worker(cls, args): 3. Create the Recipient View request object 4. Obtain the recipient_view_url for the embedded signing """ + #ds-snippet-start:eSign1Step3 envelope_args = args["envelope_args"] # 1. Create the envelope request object envelope_definition = cls.make_envelope(envelope_args) @@ -53,8 +54,10 @@ def worker(cls, args): results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) envelope_id = results.envelope_id + #ds-snippet-end:eSign1Step3 # 3. Create the Recipient View request object + #ds-snippet-start:eSign1Step4 recipient_view_request = RecipientViewRequest( authentication_method=authentication_method, client_user_id=envelope_args["signer_client_id"], @@ -63,8 +66,12 @@ def worker(cls, args): user_name=envelope_args["signer_name"], email=envelope_args["signer_email"] ) + #ds-snippet-end:eSign1Step4 + # 4. Obtain the recipient_view_url for the embedded signing # Exceptions will be caught by the calling function + + #ds-snippet-start:eSign1Step5 results = envelope_api.create_recipient_view( account_id=args["account_id"], envelope_id=envelope_id, @@ -72,8 +79,10 @@ def worker(cls, args): ) return {"envelope_id": envelope_id, "redirect_url": results.url} + #ds-snippet-end:eSign1Step5 @classmethod + #ds-snippet-start:eSign1Step2 def make_envelope(cls, args): """ Creates envelope @@ -132,3 +141,4 @@ def make_envelope(cls, args): ) return envelope_definition + #ds-snippet-end:eSign1Step2 diff --git a/app/eSignature/examples/eg002_signing_via_email/controller.py b/app/eSignature/examples/eg002_signing_via_email.py similarity index 82% rename from app/eSignature/examples/eg002_signing_via_email/controller.py rename to app/eSignature/examples/eg002_signing_via_email.py index 6ac51aa0..4378975e 100644 --- a/app/eSignature/examples/eg002_signing_via_email/controller.py +++ b/app/eSignature/examples/eg002_signing_via_email.py @@ -2,51 +2,26 @@ from os import path from docusign_esign import EnvelopesApi, EnvelopeDefinition, Document, Signer, CarbonCopy, SignHere, Tabs, Recipients -from flask import session, request - -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG - - -class Eg002Controller: - @staticmethod - def get_args(): - """Get request and session arguments""" - - # More data validation would be a good idea here - # Strip anything other than characters listed - signer_email = pattern.sub("", request.form.get("signer_email")) - signer_name = pattern.sub("", request.form.get("signer_name")) - cc_email = pattern.sub("", request.form.get("cc_email")) - cc_name = pattern.sub("", request.form.get("cc_name")) - envelope_args = { - "signer_email": signer_email, - "signer_name": signer_name, - "cc_email": cc_email, - "cc_name": cc_name, - "status": "sent", - } - args = { - "account_id": session["ds_account_id"], - "base_path": session["ds_base_path"], - "access_token": session["ds_access_token"], - "envelope_args": envelope_args - } - return args + +from ...consts import demo_docs_path, pattern +from ...jwt_helpers import create_api_client + + +class Eg002SigningViaEmailController: @classmethod - def worker(cls, args): + def worker(cls, args, doc_docx_path, doc_pdf_path): """ 1. Create the envelope request object 2. Send the envelope """ + #ds-snippet-start:eSign2Step3 envelope_args = args["envelope_args"] - # 1. Create the envelope request object - envelope_definition = cls.make_envelope(envelope_args) + # Create the envelope request object + envelope_definition = cls.make_envelope(envelope_args, doc_docx_path, doc_pdf_path) api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) - # 2. call Envelopes::create API method + # Call Envelopes::create API method # Exceptions will be caught by the calling function envelopes_api = EnvelopesApi(api_client) results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) @@ -54,9 +29,11 @@ def worker(cls, args): envelope_id = results.envelope_id return {"envelope_id": envelope_id} + #ds-snippet-end:eSign2Step3 @classmethod - def make_envelope(cls, args): + #ds-snippet-start:eSign2Step2 + def make_envelope(cls, args, doc_docx_path, doc_pdf_path): """ Creates envelope Document 1: An HTML document. @@ -83,10 +60,10 @@ def make_envelope(cls, args): doc1_b64 = base64.b64encode(bytes(cls.create_document1(args), "utf-8")).decode("ascii") # read files 2 and 3 from a local directory # The reads could raise an exception if the file is not available! - with open(path.join(demo_docs_path, DS_CONFIG["doc_docx"]), "rb") as file: + with open(path.join(demo_docs_path, doc_docx_path), "rb") as file: doc2_docx_bytes = file.read() doc2_b64 = base64.b64encode(doc2_docx_bytes).decode("ascii") - with open(path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), "rb") as file: + with open(path.join(demo_docs_path, doc_pdf_path), "rb") as file: doc3_pdf_bytes = file.read() doc3_b64 = base64.b64encode(doc3_pdf_bytes).decode("ascii") @@ -196,4 +173,5 @@ def create_document1(cls, args):

Agreed: **signature_1**/

- """ + """ + #ds-snippet-end:eSign2Step2 diff --git a/app/eSignature/examples/eg002_signing_via_email/__init__.py b/app/eSignature/examples/eg002_signing_via_email/__init__.py deleted file mode 100644 index 1b2ca36d..00000000 --- a/app/eSignature/examples/eg002_signing_via_email/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg002 diff --git a/app/eSignature/examples/eg002_signing_via_email/views.py b/app/eSignature/examples/eg002_signing_via_email/views.py deleted file mode 100644 index 124def2d..00000000 --- a/app/eSignature/examples/eg002_signing_via_email/views.py +++ /dev/null @@ -1,59 +0,0 @@ -""" Example 002: Remote signer, cc, envelope has three documents """ - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, session, Blueprint - -from .controller import Eg002Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg002" # reference (and url) for this example -eg002 = Blueprint("eg002", __name__) - - -@eg002.route("/eg002", methods=["POST"]) -@authenticate(eg=eg) -def sign_by_email(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render success response with envelopeId - """ - - # 1. Get required arguments - args = Eg002Controller.get_args() - try: - # 1. Call the worker method - results = Eg002Controller.worker(args) - except ApiException as err: - return process_error(err) - - session["envelope_id"] = results["envelope_id"] # Save for use by other examples which need an envelopeId - - # 2. Render success response with envelopeId - return render_template( - "example_done.html", - title="Envelope sent", - h1="Envelope sent", - message=f"The envelope has been created and sent!
Envelope ID {results['envelope_id']}." - ) - - -@eg002.route("/eg002", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - - return render_template( - "eg002_signing_via_email.html", - title="Signing via email", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"] - ) diff --git a/app/eSignature/examples/eg003_list_envelopes/controller.py b/app/eSignature/examples/eg003_list_envelopes.py similarity index 78% rename from app/eSignature/examples/eg003_list_envelopes/controller.py rename to app/eSignature/examples/eg003_list_envelopes.py index 9bbe2b28..96855006 100644 --- a/app/eSignature/examples/eg003_list_envelopes/controller.py +++ b/app/eSignature/examples/eg003_list_envelopes.py @@ -3,10 +3,10 @@ from docusign_esign import EnvelopesApi from flask import session -from ....docusign import create_api_client +from ...docusign import create_api_client -class Eg003Controller: +class Eg003ListEnvelopesController: @staticmethod def get_args(): """Get request and session arguments""" @@ -23,19 +23,21 @@ def worker(args): that have changed in the last 10 days """ # Exceptions will be caught by the calling function + #ds-snippet-start:eSign3Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) envelope_api = EnvelopesApi(api_client) # The Envelopes::listStatusChanges method has many options - # See https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/listStatusChanges + # See https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/liststatuschanges/ # The list status changes call requires at least a from_date OR # a set of envelopeIds. Here we filter using a from_date. # Here we set the from_date to filter envelopes for the last month # Use ISO 8601 date format # 1. Call the envelope status change method to list the envelopes - from_date = (datetime.utcnow() - timedelta(days=30)).isoformat() + from_date = (datetime.utcnow() - timedelta(days=30)).strftime('%Y-%m-%d') results = envelope_api.list_status_changes(account_id=args["account_id"], from_date=from_date) + #ds-snippet-end:eSign3Step2 return results diff --git a/app/eSignature/examples/eg003_list_envelopes/__init__.py b/app/eSignature/examples/eg003_list_envelopes/__init__.py deleted file mode 100644 index c5813bf4..00000000 --- a/app/eSignature/examples/eg003_list_envelopes/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg003 diff --git a/app/eSignature/examples/eg003_list_envelopes/views.py b/app/eSignature/examples/eg003_list_envelopes/views.py deleted file mode 100644 index ee08e298..00000000 --- a/app/eSignature/examples/eg003_list_envelopes/views.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Example 003: List envelopes in the user"s account""" - -import json -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, Blueprint - -from .controller import Eg003Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg003" # reference (and url) for this example -eg003 = Blueprint("eg003", __name__) - - -@eg003.route("/eg003", methods=["POST"]) -@authenticate(eg=eg) -def envelope_list(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Show envelopes - """ - - # 1. Get required arguments - args = Eg003Controller.get_args() - try: - # 1. Call the worker method - results = Eg003Controller.worker(args) - except ApiException as err: - return process_error(err) - # 3. Show envelopes - return render_template( - "example_done.html", - title="List envelopes results", - h1="List envelopes results", - message="Results from the Envelopes::listStatusChanges method:", - json=json.dumps(json.dumps(results.to_dict())) - ) - - -@eg003.route("/eg003", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - - return render_template( - "eg003_list_envelopes.html", - title="List changed envelopes", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) diff --git a/app/eSignature/examples/eg004_envelope_info/controller.py b/app/eSignature/examples/eg004_envelope_info.py similarity index 81% rename from app/eSignature/examples/eg004_envelope_info/controller.py rename to app/eSignature/examples/eg004_envelope_info.py index 4b2f9c02..4ec0d144 100644 --- a/app/eSignature/examples/eg004_envelope_info/controller.py +++ b/app/eSignature/examples/eg004_envelope_info.py @@ -1,10 +1,10 @@ from docusign_esign import EnvelopesApi from flask import session -from ....docusign import create_api_client +from ...docusign import create_api_client -class Eg004Controller: +class Eg004EnvelopeInfoController: @staticmethod def get_args(): """ @@ -24,10 +24,11 @@ def worker(args): """ # Exceptions will be caught by the calling function + #ds-snippet-start:eSign4Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) envelope_api = EnvelopesApi(api_client) - # 1. Call the envelope get method + # Call the envelope get method results = envelope_api.get_envelope(account_id=args["account_id"], envelope_id=args["envelope_id"]) - + #ds-snippet-end:eSign4Step2 return results diff --git a/app/eSignature/examples/eg004_envelope_info/__init__.py b/app/eSignature/examples/eg004_envelope_info/__init__.py deleted file mode 100644 index 4af46e78..00000000 --- a/app/eSignature/examples/eg004_envelope_info/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg004 diff --git a/app/eSignature/examples/eg004_envelope_info/views.py b/app/eSignature/examples/eg004_envelope_info/views.py deleted file mode 100644 index c7f87ed0..00000000 --- a/app/eSignature/examples/eg004_envelope_info/views.py +++ /dev/null @@ -1,68 +0,0 @@ -"""004: Get an envelope"s basic information and status""" - -import json -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, session, Blueprint - -from .controller import Eg004Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg004" # reference (and url) for this example -eg004 = Blueprint("eg004", __name__) - - -@eg004.route("/eg004", methods=["POST"]) -@authenticate(eg=eg) -def envelope_info(): - """ - 1. Get required arguments - 1. Call the worker method - 2. Show envelope info - """ - - if "envelope_id" in session: - # 1. Get required arguments - args = Eg004Controller.get_args() - try: - # 1. Call the worker method - results = Eg004Controller.worker(args) - except ApiException as err: - return process_error(err) - # 2.Show envelope info - return render_template( - "example_done.html", - title="Get envelope status results", - h1="Get envelope status results", - message="Results from the Envelopes::get method:", - json=json.dumps(json.dumps(results.to_dict())) - ) - else: - return render_template( - "eg004_envelope_info.html", - title="Envelope information", - envelope_ok=False, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) - - -@eg004.route("/eg004", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - - return render_template( - "eg004_envelope_info.html", - title="Envelope information", - envelope_ok="envelope_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) diff --git a/app/eSignature/examples/eg005_envelope_recipients/controller.py b/app/eSignature/examples/eg005_envelope_recipients.py similarity index 76% rename from app/eSignature/examples/eg005_envelope_recipients/controller.py rename to app/eSignature/examples/eg005_envelope_recipients.py index 983ec8af..607a2c7f 100644 --- a/app/eSignature/examples/eg005_envelope_recipients/controller.py +++ b/app/eSignature/examples/eg005_envelope_recipients.py @@ -1,10 +1,10 @@ from docusign_esign import EnvelopesApi from flask import session -from ....docusign import create_api_client +from ...docusign import create_api_client -class Eg005Controller: +class Eg005EnvelopeRecipientsController: @staticmethod def get_args(): """ @@ -18,16 +18,18 @@ def get_args(): } @staticmethod + #ds-snippet-start:eSign5Step2 def worker(args): """ - 1. Call the envelope recipients list method + Call the envelope recipients list method """ # Exceptions will be caught by the calling function api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) envelope_api = EnvelopesApi(api_client) - # 1. Call the envelope recipients list method + # Call the envelope recipients list method results = envelope_api.list_recipients(account_id=args["account_id"], envelope_id=args["envelope_id"]) return results + #ds-snippet-end:eSign5Step2 diff --git a/app/eSignature/examples/eg005_envelope_recipients/__init__.py b/app/eSignature/examples/eg005_envelope_recipients/__init__.py deleted file mode 100644 index 45102c7a..00000000 --- a/app/eSignature/examples/eg005_envelope_recipients/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg005 diff --git a/app/eSignature/examples/eg005_envelope_recipients/views.py b/app/eSignature/examples/eg005_envelope_recipients/views.py deleted file mode 100644 index d9374e62..00000000 --- a/app/eSignature/examples/eg005_envelope_recipients/views.py +++ /dev/null @@ -1,68 +0,0 @@ -"""005: List an envelope"s recipients and status""" - -import json -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, session, Blueprint - -from .controller import Eg005Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg005" # reference (and url) for this example -eg005 = Blueprint("eg005", __name__) - - -@eg005.route("/eg005", methods=["POST"]) -@authenticate(eg=eg) -def envelope_recipients(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Show recipients - """ - - if "envelope_id" in session: - # 1. Get required arguments - args = Eg005Controller.get_args() - try: - # 2. Call the worker method - results = Eg005Controller.worker(args) - except ApiException as err: - return process_error(err) - # 3. Show recipients - return render_template( - "example_done.html", - title="Envelope recipients results", - h1="List the envelope's recipients and their status", - message="Results from the EnvelopesRecipients::list method:", - json=json.dumps(json.dumps(results.to_dict())) - ) - else: - return render_template( - "eg005_envelope_recipients.html", - title="Envelope recipient information", - envelope_ok=False, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) - - -@eg005.route("/eg005", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - - return render_template( - "eg005_envelope_recipients.html", - title="Envelope recipient information", - envelope_ok="envelope_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) diff --git a/app/eSignature/examples/eg006_envelope_docs/controller.py b/app/eSignature/examples/eg006_envelope_docs.py similarity index 84% rename from app/eSignature/examples/eg006_envelope_docs/controller.py rename to app/eSignature/examples/eg006_envelope_docs.py index f20555d6..df2e5000 100644 --- a/app/eSignature/examples/eg006_envelope_docs/controller.py +++ b/app/eSignature/examples/eg006_envelope_docs.py @@ -1,10 +1,10 @@ from docusign_esign import EnvelopesApi from flask import session -from ....docusign import create_api_client +from ...docusign import create_api_client -class Eg006Controller: +class Eg006EnvelopeDocsController: @staticmethod def get_args(): """ @@ -23,12 +23,15 @@ def worker(args): 1. Call the EnvelopeDocuments::list method """ - # Exceptions will be caught by the calling function + #ds-snippet-start:eSign6Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign6Step2 + #ds-snippet-start:eSign6Step3 envelope_api = EnvelopesApi(api_client) - # 1. Call the EnvelopeDocuments::list method + # Call the EnvelopeDocuments::list method results = envelope_api.list_documents(account_id=args["account_id"], envelope_id=args["envelope_id"]) + #ds-snippet-end:eSign6Step3 return results @@ -40,7 +43,8 @@ def save_envelope_documents(results): """ standard_doc_items = [ {"name": "Combined", "type": "content", "document_id": "combined"}, - {"name": "Zip archive", "type": "zip", "document_id": "archive"}] + {"name": "Zip archive", "type": "zip", "document_id": "archive"}, + {"name": "PDF Portfolio", "type": "content", "document_id": "portfolio"}] # The certificate of completion is named "summary". # We give it a better name below. envelope_doc_items = list(map(lambda doc: diff --git a/app/eSignature/examples/eg006_envelope_docs/__init__.py b/app/eSignature/examples/eg006_envelope_docs/__init__.py deleted file mode 100644 index b2c4ff9f..00000000 --- a/app/eSignature/examples/eg006_envelope_docs/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg006 diff --git a/app/eSignature/examples/eg006_envelope_docs/views.py b/app/eSignature/examples/eg006_envelope_docs/views.py deleted file mode 100644 index f28f326d..00000000 --- a/app/eSignature/examples/eg006_envelope_docs/views.py +++ /dev/null @@ -1,73 +0,0 @@ -"""006: List an envelope"s documents""" -import json -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, session, Blueprint - -from .controller import Eg006Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg006" # reference (and url) for this example -eg006 = Blueprint("eg006", __name__) - - -@eg006.route("/eg006", methods=["POST"]) -@authenticate(eg=eg) -def envelope_docs(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Save envelope documents - 4. Show envelope documents - """ - - if "envelope_id" in session: - # 1. Get required arguments - args = Eg006Controller.get_args() - try: - # 2. Call the worker method - results = Eg006Controller.worker(args) - except ApiException as err: - return process_error(err) - - # 3. Save envelope documents - Eg006Controller.save_envelope_documents(results) - - # 4. Show envelope documents - return render_template( - "example_done.html", - title="List an envelope's documents", - h1="List an envelope's documents", - message="Results from the EnvelopeDocuments::list method:", - json=json.dumps(json.dumps(results.to_dict())) - ) - - else: - return render_template( - "eg006_envelope_docs.html", - title="Envelope documents", - envelope_ok=False, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) - - -@eg006.route("/eg006", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - - return render_template( - "eg006_envelope_docs.html", - title="Envelope documents", - envelope_ok="envelope_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) diff --git a/app/eSignature/examples/eg007_envelope_get_doc/controller.py b/app/eSignature/examples/eg007_envelope_get_doc.py similarity index 83% rename from app/eSignature/examples/eg007_envelope_get_doc/controller.py rename to app/eSignature/examples/eg007_envelope_get_doc.py index 469158cc..d2020a61 100644 --- a/app/eSignature/examples/eg007_envelope_get_doc/controller.py +++ b/app/eSignature/examples/eg007_envelope_get_doc.py @@ -1,11 +1,11 @@ from docusign_esign import EnvelopesApi from flask import request, session -from ....consts import pattern -from ....docusign import create_api_client +from ...consts import pattern +from ...docusign import create_api_client -class Eg007Controller: +class Eg007EnvelopeGetDocController: @staticmethod def get_args(): """ @@ -31,21 +31,21 @@ def worker(args): Call the envelope get method """ # Exceptions will be caught by the calling function - # Step 2 start + #ds-snippet-start:eSign7Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) - # Step 2 end - # Step 3 start + #ds-snippet-end:eSign7Step2 + + #ds-snippet-start:eSign7Step3 envelope_api = EnvelopesApi(api_client) document_id = args["document_id"] - # The SDK always stores the received file as a temp file - # Call the envelope get method - temp_file = envelope_api.get_document( + # Call the envelope get method to get the path of the temp file with the documents + temp_file_path = envelope_api.get_document( account_id=args["account_id"], document_id=document_id, envelope_id=args["envelope_id"] ) - # Step 3 end + #ds-snippet-end:eSign7Step3 doc_item = next(item for item in args["envelope_documents"]["documents"] if item["document_id"] == document_id) doc_name = doc_item["name"] @@ -67,4 +67,4 @@ def worker(args): else: mimetype = "application/octet-stream" - return {"mimetype": mimetype, "doc_name": doc_name, "data": temp_file} + return {"mimetype": mimetype, "doc_name": doc_name, "data": temp_file_path} diff --git a/app/eSignature/examples/eg007_envelope_get_doc/__init__.py b/app/eSignature/examples/eg007_envelope_get_doc/__init__.py deleted file mode 100644 index 0e6d362e..00000000 --- a/app/eSignature/examples/eg007_envelope_get_doc/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg007 diff --git a/app/eSignature/examples/eg008_create_template/controller.py b/app/eSignature/examples/eg008_create_template.py similarity index 84% rename from app/eSignature/examples/eg008_create_template/controller.py rename to app/eSignature/examples/eg008_create_template.py index 04131096..143f0bd5 100644 --- a/app/eSignature/examples/eg008_create_template/controller.py +++ b/app/eSignature/examples/eg008_create_template.py @@ -2,14 +2,14 @@ from os import path from docusign_esign import Document, Signer, CarbonCopy, SignHere, Tabs, Recipients, \ - TemplatesApi, Checkbox, List, ListItem, Number, Radio, RadioGroup, Text, EnvelopeTemplate + TemplatesApi, Checkbox, List, ListItem, Numerical, Radio, RadioGroup, Text, EnvelopeTemplate from flask import session -from ....consts import demo_docs_path, doc_file, template_name -from ....docusign import create_api_client +from ...consts import demo_docs_path, doc_file, template_name +from ...docusign import create_api_client -class Eg008Controller: +class Eg008CreateTemplateController: @staticmethod def get_args(): """ @@ -18,7 +18,11 @@ def get_args(): return { "account_id": session["ds_account_id"], "base_path": session["ds_base_path"], - "access_token": session["ds_access_token"] + "access_token": session["ds_access_token"], + "template_args": { + "doc_file": path.join(demo_docs_path, doc_file), + "template_name": template_name + } } @classmethod @@ -34,29 +38,34 @@ def worker(cls, args): # Exceptions will be caught by the calling function results = templates_api.list_templates(account_id=args["account_id"], search_text=template_name) created_new_template = False + if int(results.result_set_size) > 0: + template_id = results.envelope_templates[0].template_id results_template_name = results.envelope_templates[0].name + else: # Template not found -- so create it # 2. create the template - template_req_object = cls.make_template_req() + #ds-snippet-start:eSign8Step3 + template_req_object = cls.make_template_req(args["template_args"]) res = templates_api.create_template(account_id=args["account_id"], envelope_template=template_req_object) - # Pick the first template object within the result - results = res.templates[0] - template_id = results.template_id - results_template_name = results.name + #ds-snippet-end:eSign8Step3 + template_id = res.template_id + results_template_name = res.name created_new_template = True return { + "template_id": template_id, "template_name": results_template_name, "created_new_template": created_new_template } @classmethod - def make_template_req(cls): + #ds-snippet-start:eSign8Step2 + def make_template_req(cls, args): """Creates template req object""" # document 1 (pdf) @@ -64,7 +73,7 @@ def make_template_req(cls): # The template has two recipient roles. # recipient 1 - signer # recipient 2 - cc - with open(path.join(demo_docs_path, doc_file), "rb") as file: + with open(args["doc_file"], "rb") as file: content_bytes = file.read() base64_file_content = base64.b64encode(content_bytes).decode("ascii") @@ -130,15 +139,17 @@ def make_template_req(cls): ListItem(text="Violet", value="violet") ] ) - number1 = Number( + numerical = Numerical( document_id="1", + validation_type="Currency", page_number="1", x_position="163", y_position="260", font="helvetica", font_size="size14", - tab_label="numbersOnly", + tab_label="numericalCurrency", width="84", + height="23", required="false" ) radio_group = RadioGroup( @@ -177,7 +188,7 @@ def make_template_req(cls): sign_here_tabs=[sign_here], checkbox_tabs=[check1, check2, check3, check4], list_tabs=[list1], - number_tabs=[number1], + numerical_tabs=[numerical], radio_group_tabs=[radio_group], text_tabs=[text] ) @@ -187,9 +198,10 @@ def make_template_req(cls): documents=[document], email_subject="Please sign this document", recipients=Recipients(signers=[signer], carbon_copies=[cc]), description="Example template created via the API", - name=template_name, + name=args["template_name"], shared="false", status="created" ) return template_request + #ds-snippet-end:eSign8Step2 diff --git a/app/eSignature/examples/eg008_create_template/__init__.py b/app/eSignature/examples/eg008_create_template/__init__.py deleted file mode 100644 index 2111ed18..00000000 --- a/app/eSignature/examples/eg008_create_template/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg008 diff --git a/app/eSignature/examples/eg009_use_template/controller.py b/app/eSignature/examples/eg009_use_template.py similarity index 92% rename from app/eSignature/examples/eg009_use_template/controller.py rename to app/eSignature/examples/eg009_use_template.py index 42505d6d..ea58d34c 100644 --- a/app/eSignature/examples/eg009_use_template/controller.py +++ b/app/eSignature/examples/eg009_use_template.py @@ -1,11 +1,11 @@ from docusign_esign import EnvelopesApi, EnvelopeDefinition, TemplateRole from flask import request, session -from ....consts import pattern -from ....docusign import create_api_client +from ...consts import pattern +from ...docusign import create_api_client -class Eg009Controller: +class Eg009UseTemplateController: @staticmethod def get_args(): @@ -33,6 +33,7 @@ def get_args(): return args @classmethod + #ds-snippet-start:eSign9Step3 def worker(cls, args): """ 1. Create the envelope request object @@ -50,8 +51,10 @@ def worker(cls, args): results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) envelope_id = results.envelope_id return {"envelope_id": envelope_id} + #ds-snippet-end:eSign9Step3 @classmethod + #ds-snippet-start:eSign9Step2 def make_envelope(cls, args): """ Creates envelope @@ -82,3 +85,4 @@ def make_envelope(cls, args): # Add the TemplateRole objects to the envelope object envelope_definition.template_roles = [signer, cc] return envelope_definition + #ds-snippet-end:eSign9Step2 diff --git a/app/eSignature/examples/eg009_use_template/__init__.py b/app/eSignature/examples/eg009_use_template/__init__.py deleted file mode 100644 index 4467780d..00000000 --- a/app/eSignature/examples/eg009_use_template/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg009 diff --git a/app/eSignature/examples/eg010_send_binary_docs/controller.py b/app/eSignature/examples/eg010_send_binary_docs.py similarity index 92% rename from app/eSignature/examples/eg010_send_binary_docs/controller.py rename to app/eSignature/examples/eg010_send_binary_docs.py index b04d19e6..db903259 100644 --- a/app/eSignature/examples/eg010_send_binary_docs/controller.py +++ b/app/eSignature/examples/eg010_send_binary_docs.py @@ -4,11 +4,11 @@ import requests from flask import request, session -from ....consts import demo_docs_path, pattern -from ....ds_config import DS_CONFIG +from ...consts import demo_docs_path, pattern +from ...ds_config import DS_CONFIG -class Eg010Controller: +class Eg010SendBinaryDocsController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -47,10 +47,11 @@ def worker(cls, args): signer_email, signer_name, cc_email, cc_name """ - # Step 1. Make the envelope JSON request body + # Make the envelope JSON request body + #ds-snippet-start:eSign10Step3 envelope_json = cls.make_envelope_json(args["envelope_args"]) - # Step 2. Gather documents and their headers + # Gather documents and their headers # Read files 2 and 3 from a local directory # The reads could raise an exception if the file is not available! # Note: the fles are not binary encoded! @@ -72,7 +73,7 @@ def worker(cls, args): "bytes": doc3_pdf_bytes} ] - # Step 3. Create the multipart body + # Create the multipart body CRLF = b"\r\n" boundary = b"multipartboundary_multipartboundary" hyphens = b"--" @@ -99,20 +100,28 @@ def worker(cls, args): # Add closing boundary req_body = b"".join([req_body, CRLF, hyphens, boundary, hyphens, CRLF]) + #ds-snippet-end:eSign10Step3 - # Step 2. call Envelopes::create API method + # Call Envelopes::create API method # Exceptions will be caught by the calling function + #ds-snippet-start:eSign10Step2 + headers = { + "Authorization": "bearer " + args['access_token'], + "Accept": "application/json", + "Content-Type": f"multipart/form-data; boundary={boundary.decode('utf-8')}" + } + #ds-snippet-end:eSign10Step2 + + #ds-snippet-start:eSign10Step4 results = requests.post( url=f"{args['base_path']}/v2.1/accounts/{args['account_id']}/envelopes", - headers={ - "Authorization": "bearer " + args['access_token'], - "Accept": "application/json", - "Content-Type": f"multipart/form-data; boundary={boundary.decode('utf-8')}" - }, + headers=headers, data=req_body ) return {"status_code": results.status_code, "results": results.json()} + #ds-snippet-end:eSign10Step4 + #ds-snippet-start:eSign10Step3 @classmethod def make_envelope_json(cls, args): """ @@ -225,3 +234,4 @@ def create_document1(cls, args): """ + #ds-snippet-end:eSign10Step3 diff --git a/app/eSignature/examples/eg010_send_binary_docs/__init__.py b/app/eSignature/examples/eg010_send_binary_docs/__init__.py deleted file mode 100644 index babbba1b..00000000 --- a/app/eSignature/examples/eg010_send_binary_docs/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg010 diff --git a/app/eSignature/examples/eg011_embedded_sending.py b/app/eSignature/examples/eg011_embedded_sending.py new file mode 100644 index 00000000..9e42084b --- /dev/null +++ b/app/eSignature/examples/eg011_embedded_sending.py @@ -0,0 +1,268 @@ +import base64 +from os import path + +from docusign_esign import EnvelopesApi, EnvelopesApi, EnvelopeDefinition, \ + Document, Signer, CarbonCopy, SignHere, Tabs, Recipients, EnvelopeViewRequest, EnvelopeViewSettings, \ + EnvelopeViewRecipientSettings, EnvelopeViewDocumentSettings, EnvelopeViewTaggerSettings, EnvelopeViewTemplateSettings +from flask import url_for, session, request + +from ...consts import pattern, demo_docs_path +from ...docusign import create_api_client + + +class Eg011EmbeddedSendingController: + + @staticmethod + def get_args(): + """Get required session and request arguments""" + # More data validation would be a good idea here + # Strip anything other than characters listed + signer_email = pattern.sub("", request.form.get("signer_email")) + signer_name = pattern.sub("", request.form.get("signer_name")) + cc_email = pattern.sub("", request.form.get("cc_email")) + cc_name = pattern.sub("", request.form.get("cc_name")) + starting_view = pattern.sub("", request.form.get("starting_view")) + + envelope_args = { + "signer_email": signer_email, + "signer_name": signer_name, + "cc_email": cc_email, + "cc_name": cc_name, + "status": "created", + } + args = { + "starting_view": starting_view, + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_args": envelope_args, + "ds_return_url": url_for("ds.ds_return", _external=True), + "starting_view": starting_view, + } + return args + + @classmethod + def worker(cls, args, doc_docx_path, doc_pdf_path): + """ + This function does the work of creating the envelope in + draft mode and returning a URL for the sender"s view + """ + + # Step 2. Create the envelope with "created" (draft) status + envelope = cls.create_envelope(args, doc_docx_path, doc_pdf_path) + envelope_id = envelope.envelope_id + + # Step 3. Create the sender view + sender_view_url = cls.create_sender_view(args, envelope_id) + + return {"envelope_id": envelope_id, "redirect_url": sender_view_url} + + @classmethod + #ds-snippet-start:eSign11Step3 + def create_sender_view(cls, args, envelope_id): + view_request = cls.make_envelope_view_request(args) + # Exceptions will be caught by the calling function + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + + envelope_api = EnvelopesApi(api_client) + sender_view = envelope_api.create_sender_view( + account_id=args["account_id"], + envelope_id=envelope_id, + envelope_view_request=view_request + ) + + # Switch to Recipient and Documents view if requested by the user + url = sender_view.url + + return url + + @classmethod + def make_envelope_view_request(cls, args): + view_request = EnvelopeViewRequest( + return_url=args["ds_return_url"], + view_access="envelope", + settings=EnvelopeViewSettings( + starting_screen=args["starting_view"], + send_button_action="send", + show_back_button="false", + back_button_action="previousPage", + show_header_actions="false", + show_discard_action="false", + lock_token="", + recipient_settings=EnvelopeViewRecipientSettings( + show_edit_recipients="false", + show_contacts_list="false" + ), + document_settings=EnvelopeViewDocumentSettings( + show_edit_documents="false", + show_edit_document_visibility="false", + show_edit_pages="false" + ), + tagger_settings=EnvelopeViewTaggerSettings( + palette_sections="default", + palette_default="custom" + ), + template_settings=EnvelopeViewTemplateSettings( + show_matching_templates_prompt="true" + ) + ) + ) + + return view_request + #ds-snippet-end:eSign11Step3 + + @classmethod + #ds-snippet-start:eSign11Step2 + def create_envelope(cls, args, doc_docx_path, doc_pdf_path): + envelope_args = args["envelope_args"] + # Create the envelope request object + envelope_definition = cls.make_envelope(envelope_args, doc_docx_path, doc_pdf_path) + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + # Call Envelopes::create API method + # Exceptions will be caught by the calling function + envelopes_api = EnvelopesApi(api_client) + return envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + + @classmethod + def make_envelope(cls, args, doc_docx_path, doc_pdf_path): + """ + Creates envelope + Document 1: An HTML document. + Document 2: A Word .docx document. + Document 3: A PDF document. + DocuSign will convert all of the documents to the PDF format. + The recipients" field tags are placed using anchor strings. + """ + + # document 1 (html) has sign here anchor tag **signature_1** + # document 2 (docx) has sign here anchor tag /sn1/ + # document 3 (pdf) has sign here anchor tag /sn1/ + # + # The envelope has two recipients. + # recipient 1 - signer + # recipient 2 - cc + # The envelope will be sent first to the signer. + # After it is signed, a copy is sent to the cc person. + + # create the envelope definition + env = EnvelopeDefinition( + email_subject="Please sign this document set" + ) + doc1_b64 = base64.b64encode(bytes(cls.create_document1(args), "utf-8")).decode("ascii") + # read files 2 and 3 from a local directory + # The reads could raise an exception if the file is not available! + with open(path.join(demo_docs_path, doc_docx_path), "rb") as file: + doc2_docx_bytes = file.read() + doc2_b64 = base64.b64encode(doc2_docx_bytes).decode("ascii") + with open(path.join(demo_docs_path, doc_pdf_path), "rb") as file: + doc3_pdf_bytes = file.read() + doc3_b64 = base64.b64encode(doc3_pdf_bytes).decode("ascii") + + # Create the document models + document1 = Document( # create the DocuSign document object + document_base64=doc1_b64, + name="Order acknowledgement", # can be different from actual file name + file_extension="html", # many different document types are accepted + document_id="1" # a label used to reference the doc + ) + document2 = Document( # create the DocuSign document object + document_base64=doc2_b64, + name="Battle Plan", # can be different from actual file name + file_extension="docx", # many different document types are accepted + document_id="2" # a label used to reference the doc + ) + document3 = Document( # create the DocuSign document object + document_base64=doc3_b64, + name="Lorem Ipsum", # can be different from actual file name + file_extension="pdf", # many different document types are accepted + document_id="3" # a label used to reference the doc + ) + # The order in the docs array determines the order in the envelope + env.documents = [document1, document2, document3] + + # Create the signer recipient model + signer1 = Signer( + email=args["signer_email"], + name=args["signer_name"], + recipient_id="1", + routing_order="1" + ) + # routingOrder (lower means earlier) determines the order of deliveries + # to the recipients. Parallel routing order is supported by using the + # same integer as the order for two or more recipients. + + # create a cc recipient to receive a copy of the documents + cc1 = CarbonCopy( + email=args["cc_email"], + name=args["cc_name"], + recipient_id="2", + routing_order="2" + ) + + # Create signHere fields (also known as tabs) on the documents, + # We"re using anchor (autoPlace) positioning + # + # The DocuSign platform searches throughout your envelope"s + # documents for matching anchor strings. So the + # signHere2 tab will be used in both document 2 and 3 since they + # use the same anchor string for their "signer 1" tabs. + sign_here1 = SignHere( + anchor_string="**signature_1**", + anchor_units="pixels", + anchor_y_offset="10", + anchor_x_offset="20" + ) + + sign_here2 = SignHere( + anchor_string="/sn1/", + anchor_units="pixels", + anchor_y_offset="10", + anchor_x_offset="20" + ) + + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object wants arrays of the different field/tab types + signer1.tabs = Tabs(sign_here_tabs=[sign_here1, sign_here2]) + + # Add the recipients to the envelope object + recipients = Recipients(signers=[signer1], carbon_copies=[cc1]) + env.recipients = recipients + + # Request that the envelope be sent by setting |status| to "sent". + # To request that the envelope be created as a draft, set to "created" + env.status = args["status"] + + return env + + @classmethod + def create_document1(cls, args): + """ Creates document 1 -- an html document""" + + return f""" + + + + + + +

World Wide Corp

+

Order Processing Division

+

Ordered by {args["signer_name"]}

+

Email: {args["signer_email"]}

+

Copy to: {args["cc_name"]}, {args["cc_email"]}

+

+ Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. + Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. + Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. + Donut jujubes oat cake jelly-o. + Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake. +

+ +

Agreed: **signature_1**/

+ + + """ + #ds-snippet-end:eSign11Step2 diff --git a/app/eSignature/examples/eg011_embedded_sending/__init__.py b/app/eSignature/examples/eg011_embedded_sending/__init__.py deleted file mode 100644 index 88a815c0..00000000 --- a/app/eSignature/examples/eg011_embedded_sending/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg011 diff --git a/app/eSignature/examples/eg011_embedded_sending/controller.py b/app/eSignature/examples/eg011_embedded_sending/controller.py deleted file mode 100644 index 87ec0420..00000000 --- a/app/eSignature/examples/eg011_embedded_sending/controller.py +++ /dev/null @@ -1,69 +0,0 @@ -from docusign_esign import EnvelopesApi, ReturnUrlRequest -from flask import url_for, session, request - -from ..eg002_signing_via_email.controller import Eg002Controller -from ....consts import pattern -from ....docusign import create_api_client - - -class Eg011Controller: - - @staticmethod - def get_args(): - """Get required session and request arguments""" - # More data validation would be a good idea here - # Strip anything other than characters listed - signer_email = pattern.sub("", request.form.get("signer_email")) - signer_name = pattern.sub("", request.form.get("signer_name")) - cc_email = pattern.sub("", request.form.get("cc_email")) - cc_name = pattern.sub("", request.form.get("cc_name")) - starting_view = pattern.sub("", request.form.get("starting_view")) - - envelope_args = { - "signer_email": signer_email, - "signer_name": signer_name, - "cc_email": cc_email, - "cc_name": cc_name, - "status": "sent", - } - args = { - "starting_view": starting_view, - "account_id": session["ds_account_id"], - "base_path": session["ds_base_path"], - "access_token": session["ds_access_token"], - "envelope_args": envelope_args, - "ds_return_url": url_for("ds.ds_return", _external=True), - } - return args - - @staticmethod - def worker(args): - """ - This function does the work of creating the envelope in - draft mode and returning a URL for the sender"s view - """ - - # Step 1. Create the envelope with "created" (draft) status - args["envelope_args"]["status"] = "created" - # Using worker from example 002 - results = Eg002Controller.worker(args) - envelope_id = results["envelope_id"] - - # Step 2. Create the sender view - view_request = ReturnUrlRequest(return_url=args["ds_return_url"]) - # Exceptions will be caught by the calling function - api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) - - envelope_api = EnvelopesApi(api_client) - results = envelope_api.create_sender_view( - account_id=args["account_id"], - envelope_id=envelope_id, - return_url_request=view_request - ) - - # Switch to Recipient and Documents view if requested by the user - url = results.url - if args["starting_view"] == "recipient": - url = url.replace("send=1", "send=0") - - return {"envelope_id": envelope_id, "redirect_url": url} diff --git a/app/eSignature/examples/eg011_embedded_sending/views.py b/app/eSignature/examples/eg011_embedded_sending/views.py deleted file mode 100644 index d0218e05..00000000 --- a/app/eSignature/examples/eg011_embedded_sending/views.py +++ /dev/null @@ -1,55 +0,0 @@ -"""011: Embedded sending: Remote signer, cc, envelope has three documents""" - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, redirect, Blueprint - -from .controller import Eg011Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg011" # reference (and url) for this example -eg011 = Blueprint("eg011", __name__) - - -@eg011.route("/eg011", methods=["POST"]) -@authenticate(eg=eg) -def embedded_sending(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Redirect user to NDSE view - """ - - # 1. Get required arguments - args = Eg011Controller.get_args() - try: - # 2. Call the worker method - results = Eg011Controller.worker(args) - except ApiException as err: - return process_error(err) - - # Redirect the user to the NDSE view - # Don"t use an iFrame! - # State can be stored/recovered using the framework"s session or a - # query parameter on the returnUrl (see the makeRecipientViewRequest method) - return redirect(results["redirect_url"]) - - -@eg011.route("/eg011", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - - return render_template( - "eg011_embedded_sending.html", - title="Embedded Sending", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"] - ) diff --git a/app/eSignature/examples/eg012_embedded_console/controller.py b/app/eSignature/examples/eg012_embedded_console.py similarity index 90% rename from app/eSignature/examples/eg012_embedded_console/controller.py rename to app/eSignature/examples/eg012_embedded_console.py index 982d85c6..8c3ba6f9 100644 --- a/app/eSignature/examples/eg012_embedded_console/controller.py +++ b/app/eSignature/examples/eg012_embedded_console.py @@ -1,11 +1,11 @@ from docusign_esign import EnvelopesApi, ConsoleViewRequest from flask import session, url_for, request -from ....consts import pattern -from ....docusign import create_api_client +from ...consts import pattern +from ...docusign import create_api_client -class Eg012Controller: +class Eg012EmbeddedConsoleController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -34,6 +34,7 @@ def worker(args): # with the NDSE. It is usually the case that the # user will never "finish" with the NDSE. # Assume that control will not be passed back to your app. + #ds-snippet-start:eSign12Step2 view_request = ConsoleViewRequest(return_url=args["ds_return_url"]) if args["starting_view"] == "envelope" and args["envelope_id"]: view_request.envelope_id = args["envelope_id"] @@ -45,4 +46,5 @@ def worker(args): envelope_api = EnvelopesApi(api_client) results = envelope_api.create_console_view(account_id=args["account_id"], console_view_request=view_request) url = results.url + #ds-snippet-end:eSign12Step2 return {"redirect_url": url} diff --git a/app/eSignature/examples/eg012_embedded_console/__init__.py b/app/eSignature/examples/eg012_embedded_console/__init__.py deleted file mode 100644 index 37211f9c..00000000 --- a/app/eSignature/examples/eg012_embedded_console/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg012 diff --git a/app/eSignature/examples/eg012_embedded_console/views.py b/app/eSignature/examples/eg012_embedded_console/views.py deleted file mode 100644 index c4b1e48e..00000000 --- a/app/eSignature/examples/eg012_embedded_console/views.py +++ /dev/null @@ -1,54 +0,0 @@ -"""012: Embedded console""" - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, redirect, session, Blueprint - -from .controller import Eg012Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg012" # reference (and url) for this example -eg012 = Blueprint("eg012", __name__) - - -@eg012.route("/eg012", methods=["POST"]) -@authenticate(eg=eg) -def embedded_console(): - """ - 1. Get required args - 2. Call the worker method - 3. Redirect user to NDSE view - """ - - # 1. Get required args - args = Eg012Controller.get_args() - try: - # 2. Call the worker method - results = Eg012Controller.worker(args) - except ApiException as err: - return process_error(err) - - # 3. Redirect the user to the NDSE view - # Don"t use an iFrame! - # State can be stored/recovered using the framework"s session - return redirect(results["redirect_url"]) - - -@eg012.route("/eg012", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - - envelope_id = "envelope_id" in session and session["envelope_id"] - return render_template( - "eg012_embedded_console.html", - title="Embedded Console", - envelope_ok=envelope_id, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) diff --git a/app/eSignature/examples/eg013_add_doc_to_template/controller.py b/app/eSignature/examples/eg013_add_doc_to_template.py similarity index 86% rename from app/eSignature/examples/eg013_add_doc_to_template/controller.py rename to app/eSignature/examples/eg013_add_doc_to_template.py index 11083dbb..0f8e3d2a 100644 --- a/app/eSignature/examples/eg013_add_doc_to_template/controller.py +++ b/app/eSignature/examples/eg013_add_doc_to_template.py @@ -4,11 +4,11 @@ CompositeTemplate, InlineTemplate, ServerTemplate, RecipientViewRequest from flask import url_for, session, request -from ....consts import signer_client_id, pattern -from ....docusign import create_api_client +from ...consts import signer_client_id, pattern +from ...docusign import create_api_client -class Eg013Controller: +class Eg013AddDocToTemplateController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -51,19 +51,22 @@ def worker(cls, args): 3. Make the recipient view request object 4. Get the recipient view (embedded signing) url """ + #ds-snippet-start:eSign13Step3 envelope_args = args["envelope_args"] - # 1. Create the envelope request object + # Create the envelope request object envelope_definition = cls.make_envelope(envelope_args) - # 2. call Envelopes::create API method + # Call Envelopes::create API method # Exceptions will be caught by the calling function api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) envelope_api = EnvelopesApi(api_client) results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) envelope_id = results.envelope_id + #ds-snippet-end:eSign13Step3 - # 3. Create the Recipient View request object + # Create the Recipient View request object + #ds-snippet-start:eSign13Step4 authentication_method = "None" # How is this application authenticating # the signer? See the "authenticationMethod" definition # https://goo.gl/qUhGTm @@ -75,7 +78,7 @@ def worker(cls, args): user_name=envelope_args["signer_name"], email=envelope_args["signer_email"] ) - # 4. Obtain the recipient_view_url for the embedded signing + # Obtain the recipient_view_url for the embedded signing # Exceptions will be caught by the calling function results = envelope_api.create_recipient_view( account_id=args["account_id"], @@ -84,8 +87,10 @@ def worker(cls, args): ) return {"envelope_id": envelope_id, "redirect_url": results.url} + #ds-snippet-end:eSign13Step4 @classmethod + #ds-snippet-start:eSign13Step2 def make_envelope(cls, args): """ Creates envelope @@ -98,8 +103,8 @@ def make_envelope(cls, args): 2. An additional document which is a custom HTML source document """ - # 1. Create Recipients for server template. Note that Recipients object - # is used, not TemplateRole + # Create Recipients for server template. Note that Recipients object + # is used, not TemplateRole # # Create a signer recipient for the signer role of the server template signer1 = Signer( @@ -124,7 +129,7 @@ def make_envelope(cls, args): signers=[signer1] ) - # 2. create a composite template for the Server template + roles + # Create a composite template for the Server template + roles comp_template1 = CompositeTemplate( composite_template_id="1", server_templates=[ @@ -132,7 +137,7 @@ def make_envelope(cls, args): ], # Add the roles via an inlineTemplate inline_templates=[ - InlineTemplate(sequence="1", + InlineTemplate(sequence="2", recipients=recipients_server_template) ] ) @@ -140,8 +145,8 @@ def make_envelope(cls, args): # Next, create the second composite template that will # include the new document. # - # 3. Create the signer recipient for the added document - # starting with the tab definition: + # Create the signer recipient for the added document + # starting with the tab definition: sign_here1 = SignHere( anchor_string="**signature_1**", anchor_y_offset="10", @@ -150,7 +155,7 @@ def make_envelope(cls, args): ) signer1_tabs = Tabs(sign_here_tabs=[sign_here1]) - # 4. Create Signer definition for the added document + # Create Signer definition for the added document signer1_added_doc = Signer( email=args["signer_email"], name=args["signer_name"], @@ -159,11 +164,11 @@ def make_envelope(cls, args): client_user_id=args["signer_client_id"], tabs=signer1_tabs ) - # 5. The Recipients object for the added document. - # Using cc1 definition from above. + # The Recipients object for the added document. + # using cc1 definition from above. recipients_added_doc = Recipients( carbon_copies=[cc1], signers=[signer1_added_doc]) - # 6. Create the HTML document that will be added to the envelope + # Create the HTML document that will be added to the envelope doc1_b64 = base64.b64encode(bytes(cls.create_document1(args), "utf-8")) \ .decode("ascii") doc1 = Document( @@ -173,16 +178,16 @@ def make_envelope(cls, args): file_extension="html", document_id="1" ) - # 6. create a composite template for the added document + # Create a composite template for the added document comp_template2 = CompositeTemplate( composite_template_id="2", # Add the recipients via an inlineTemplate inline_templates=[ - InlineTemplate(sequence="2", recipients=recipients_added_doc) + InlineTemplate(sequence="1", recipients=recipients_added_doc) ], document=doc1 ) - # 7. create the envelope definition with the composited templates + # Create the envelope definition with the composited templates envelope_definition = EnvelopeDefinition( status="sent", composite_templates=[comp_template1, comp_template2] @@ -216,3 +221,4 @@ def create_document1(cls, args): """ + #ds-snippet-end:eSign13Step2 diff --git a/app/eSignature/examples/eg013_add_doc_to_template/__init__.py b/app/eSignature/examples/eg013_add_doc_to_template/__init__.py deleted file mode 100644 index 525e5b86..00000000 --- a/app/eSignature/examples/eg013_add_doc_to_template/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg013 diff --git a/app/eSignature/examples/eg013_add_doc_to_template/views.py b/app/eSignature/examples/eg013_add_doc_to_template/views.py deleted file mode 100644 index 0a7e8654..00000000 --- a/app/eSignature/examples/eg013_add_doc_to_template/views.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Example 013: Embedded Signing from template with added document""" - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, redirect, session, Blueprint - -from .controller import Eg013Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg013" # reference (and url) for this example -eg013 = Blueprint("eg013", __name__) - - -@eg013.route("/eg013", methods=["POST"]) -@authenticate(eg=eg) -def add_doc_template(): - """ - 1. Check the presence of a saved template_id - 2. Get required arguments - 3. Call the worker method - 4. Redirect user to Signing ceremory - """ - # 1. Check the presence of a saved template_id - if "template_id" in session: - # 2. Get required arguments - args = Eg013Controller.get_args() - try: - # 3. Call the worker method - results = Eg013Controller.worker(args) - except ApiException as err: - return process_error(err) - - # 4. Redirect the user to the embedded signing - # Don"t use an iFrame! - # State can be stored/recovered using the framework"s session - return redirect(results["redirect_url"]) - - else: - return render_template( - "eg013_add_doc_to_template.html", - title="Use embedded signing from template and extra doc", - template_ok=False, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) - - -@eg013.route("/eg013", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - - return render_template( - "eg013_add_doc_to_template.html", - title="Use embedded signing from template and extra doc", - template_ok="template_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"] - ) diff --git a/app/eSignature/examples/eg014_collect_payment/controller.py b/app/eSignature/examples/eg014_collect_payment.py similarity index 97% rename from app/eSignature/examples/eg014_collect_payment/controller.py rename to app/eSignature/examples/eg014_collect_payment.py index eede465c..63b3b4ca 100644 --- a/app/eSignature/examples/eg014_collect_payment/controller.py +++ b/app/eSignature/examples/eg014_collect_payment.py @@ -5,12 +5,12 @@ ListItem, List, FormulaTab, PaymentLineItem, PaymentDetails from flask import request, session -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG -class Eg014Controller: +class Eg014CollectPaymentController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -50,10 +50,12 @@ def worker(cls, args): # 2. call Envelopes::create API method # Exceptions will be caught by the calling function + #ds-snippet-start:eSign14Step4 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) envelope_api = EnvelopesApi(api_client) results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + #ds-snippet-end:eSign14Step4 envelope_id = results.envelope_id # app.logger.info(f"Envelope was created. EnvelopeId {envelope_id}") @@ -61,6 +63,7 @@ def worker(cls, args): return {"envelope_id": envelope_id} @classmethod + #ds-snippet-start:eSign14Step3 def make_envelope(cls, args): """ This function creates the envelope definition for the @@ -272,3 +275,4 @@ def make_envelope(cls, args): envelope_definition.status = args["status"] return envelope_definition + #ds-snippet-end:eSign14Step3 diff --git a/app/eSignature/examples/eg014_collect_payment/__init__.py b/app/eSignature/examples/eg014_collect_payment/__init__.py deleted file mode 100644 index 82422776..00000000 --- a/app/eSignature/examples/eg014_collect_payment/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg014 diff --git a/app/eSignature/examples/eg014_collect_payment/views.py b/app/eSignature/examples/eg014_collect_payment/views.py deleted file mode 100644 index d9791dfd..00000000 --- a/app/eSignature/examples/eg014_collect_payment/views.py +++ /dev/null @@ -1,61 +0,0 @@ -""" Example 014: Remote signer, cc; envelope has an order form """ - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, Blueprint - -from .controller import Eg014Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg014" # reference (and url) for this example -eg014 = Blueprint("eg014", __name__) - - -@eg014.route("/eg014", methods=["POST"]) -@authenticate(eg=eg) -def collect_payment(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render success response - """ - - # 1. Get required arguments - args = Eg014Controller.get_args() - try: - # 2. Call the worker method - results = Eg014Controller.worker(args) - except ApiException as err: - return process_error(err) - - # 3. Render success response - return render_template( - "example_done.html", - title="Envelope sent", - h1="Envelope sent", - message=f"""The envelope has been created and sent!
Envelope ID {results["envelope_id"]}.""" - ) - - -@eg014.route("/eg014", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - - gateway = DS_CONFIG["gateway_account_id"] - gateway_ok = gateway and len(gateway) > 25 - - return render_template( - "eg014_collect_payment.html", - title="Order form with payment", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"], - gateway_ok=gateway_ok - ) diff --git a/app/eSignature/examples/eg015_envelope_tab_data/controller.py b/app/eSignature/examples/eg015_envelope_tab_data.py similarity index 78% rename from app/eSignature/examples/eg015_envelope_tab_data/controller.py rename to app/eSignature/examples/eg015_envelope_tab_data.py index 6c1ba7df..3b830bee 100644 --- a/app/eSignature/examples/eg015_envelope_tab_data/controller.py +++ b/app/eSignature/examples/eg015_envelope_tab_data.py @@ -1,10 +1,10 @@ from docusign_esign import EnvelopesApi from flask import session -from ....docusign import create_api_client +from ...docusign import create_api_client -class Eg015Controller: +class Eg015EnvelopeTabDateController: @staticmethod def get_args(): """Get required session arguments""" @@ -22,8 +22,11 @@ def worker(args): """ # Exceptions will be caught by the calling function + #ds-snippet-start:eSign15Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign15Step2 + #ds-snippet-start:eSign15Step3 envelopes_api = EnvelopesApi(api_client) results = envelopes_api.get_form_data(account_id=args["account_id"], envelope_id=args["envelope_id"]) - + #ds-snippet-end:eSign15Step3 return results diff --git a/app/eSignature/examples/eg015_envelope_tab_data/__init__.py b/app/eSignature/examples/eg015_envelope_tab_data/__init__.py deleted file mode 100644 index c0675101..00000000 --- a/app/eSignature/examples/eg015_envelope_tab_data/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg015 diff --git a/app/eSignature/examples/eg015_envelope_tab_data/views.py b/app/eSignature/examples/eg015_envelope_tab_data/views.py deleted file mode 100644 index 886526dd..00000000 --- a/app/eSignature/examples/eg015_envelope_tab_data/views.py +++ /dev/null @@ -1,74 +0,0 @@ -"""015: Get an envelope"s tab information data""" - -import json -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, session, Blueprint - -from .controller import Eg015Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg015" # Reference (and URL) for this example -eg015 = Blueprint("eg015", __name__) - - -@eg015.route("/eg015", methods=["POST"]) -@authenticate(eg=eg) -def envelope_tab_data(): - """ - 1. Check presence of envelope_id in session - 2. Get required arguments - 3. Call the worker method - 4. Show Envelope tab data results - """ - - # 1. Check presence of envelope_id in session - if "envelope_id" in session: - - # 2. Get required arguments - args = Eg015Controller.get_args() - - try: - # 3. Call the worker method - results = Eg015Controller.worker(args) - except ApiException as err: - return process_error(err) - - # 4.Show Envelope tab data results - return render_template( - "example_done.html", - title="Get envelope tab data results", - h1="Get envelope tab data results", - message="Results from the Envelopes::formData GET method:", - json=json.dumps(json.dumps(results.to_dict())) - ) - - else: - return render_template( - "eg015_envelope_tab_data.html", - title="Envelope Tab Data", - envelope_ok=False, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) - - -@eg015.route("/eg015", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - - return render_template( - "eg015_envelope_tab_data.html", - title="Envelope information", - envelope_ok="envelope_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) diff --git a/app/eSignature/examples/eg016_set_tab_values/controller.py b/app/eSignature/examples/eg016_set_tab_values.py similarity index 76% rename from app/eSignature/examples/eg016_set_tab_values/controller.py rename to app/eSignature/examples/eg016_set_tab_values.py index 4b187a0c..91aecdbb 100644 --- a/app/eSignature/examples/eg016_set_tab_values/controller.py +++ b/app/eSignature/examples/eg016_set_tab_values.py @@ -2,15 +2,15 @@ from os import path from docusign_esign import EnvelopesApi, EnvelopeDefinition, Document, Signer, SignHere, Tabs, Recipients, \ - CustomFields, TextCustomField, Text, RecipientViewRequest + Numerical, LocalePolicyTab, CustomFields, TextCustomField, Text, RecipientViewRequest from flask import current_app as app, session, url_for, request -from ....consts import demo_docs_path, authentication_method, signer_client_id, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG +from ...consts import demo_docs_path, authentication_method, signer_client_id, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG -class Eg016Controller: +class Eg016SetTabValuesController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -23,6 +23,7 @@ def get_args(): "signer_name": signer_name, "signer_client_id": signer_client_id, "ds_return_url": url_for("ds.ds_return", _external=True), + "doc_file": path.join(demo_docs_path, DS_CONFIG["doc_salary_docx"]) } args = { "account_id": session["ds_account_id"], @@ -41,23 +42,27 @@ def worker(cls, args): 4. Obtain the recipient_view_url for the embedded signing """ envelope_args = args["envelope_args"] - # 1. Create the envelope request object + # Create the envelope request object envelope_definition = cls.make_envelope(envelope_args) - # 2. call Envelopes::create API method + # Call Envelopes::create API method # Exceptions will be caught by the calling function + #ds-snippet-start:eSign16Step2 api_client = create_api_client( base_path=args["base_path"], access_token=args["access_token"] ) + #ds-snippet-end:eSign16Step2 + #ds-snippet-start:eSign16Step4 envelopes_api = EnvelopesApi(api_client) results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) envelope_id = results.envelope_id - app.logger.info(f"Envelope was created. EnvelopeId {envelope_id}") + #ds-snippet-end:eSign16Step4 - # 3. Create the RecipientViewRequest object + # Create the RecipientViewRequest object + #ds-snippet-start:eSign16Step5 recipient_view_request = RecipientViewRequest( authentication_method=authentication_method, client_user_id=envelope_args["signer_client_id"], @@ -65,7 +70,7 @@ def worker(cls, args): return_url=envelope_args["ds_return_url"], user_name=envelope_args["signer_name"], email=envelope_args["signer_email"] ) - # 4. Obtain the recipient view URL for the embedded signing + # Obtain the recipient view URL for the embedded signing # Exceptions will be caught by the calling function results = envelopes_api.create_recipient_view( account_id=args["account_id"], @@ -74,8 +79,10 @@ def worker(cls, args): ) return {"envelope_id": envelope_id, "redirect_url": results.url} + #ds-snippet-end:eSign16Step5 @classmethod + #ds-snippet-start:eSign16Step3 def make_envelope(cls, args): """ Creates envelope @@ -88,7 +95,7 @@ def make_envelope(cls, args): # # The envelope has one recipient: # recipient 1 - signer - with open(path.join(demo_docs_path, DS_CONFIG["doc_salary_docx"]), "rb") as file: + with open(args["doc_file"], "rb") as file: content_bytes = file.read() base64_file_content = base64.b64encode(content_bytes).decode("ascii") @@ -129,32 +136,43 @@ def make_envelope(cls, args): bold="true", value=args["signer_name"], locked="false", tab_id="familar_name", tab_label="Familiar name") + + locale_policy_tab = LocalePolicyTab( + culture_name="en-US", + currency_code="usd", + currency_positive_format="csym_1_comma_234_comma_567_period_89", + currency_negative_format="minus_csym_1_comma_234_comma_567_period_89", + use_long_currency_format="true" + ) - salary = 123000 - - text_salary = Text( - anchor_string="/salary/", - anchor_units="pixels", - anchor_y_offset="-9", - anchor_x_offset="5", + numerical_salary = Numerical( + page_number="1", + document_id="1", + x_position="210", + y_position="235", + validation_type="Currency", font="helvetica", font_size="size11", bold="true", - value="${:.2f}".format(salary), - locked="true", + locked="false", + height="23", tab_id="salary", - tab_label="Salary") + tab_label="Salary", + numerical_value="123000", + locale_policy=locale_policy_tab + + ) salary_custom_field = TextCustomField( name="salary", required="false", show="true", # Yes, include in the CoC - value=str(salary) + value=str(123000) ) cf = CustomFields(text_custom_fields=[salary_custom_field]) # Add the tabs model (including the SignHere tab) to the signer # The Tabs object wants arrays of the different field/tab types - signer.tabs = Tabs(sign_here_tabs=[sign_here], text_tabs=[text_legal, text_familar, text_salary]) + signer.tabs = Tabs(sign_here_tabs=[sign_here], text_tabs=[text_legal, text_familar], numerical_tabs=[numerical_salary]) # Create the top level envelope definition and populate it envelope_definition = EnvelopeDefinition( @@ -167,3 +185,4 @@ def make_envelope(cls, args): ) return envelope_definition + #ds-snippet-end:eSign16Step3 diff --git a/app/eSignature/examples/eg016_set_tab_values/__init__.py b/app/eSignature/examples/eg016_set_tab_values/__init__.py deleted file mode 100644 index 7f301c03..00000000 --- a/app/eSignature/examples/eg016_set_tab_values/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg016 diff --git a/app/eSignature/examples/eg016_set_tab_values/views.py b/app/eSignature/examples/eg016_set_tab_values/views.py deleted file mode 100644 index 599780e8..00000000 --- a/app/eSignature/examples/eg016_set_tab_values/views.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Example 016: Set Tab Values""" - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, redirect, session, Blueprint - -from .controller import Eg016Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg016" # Reference (and URL) for this example -eg016 = Blueprint("eg016", __name__) - - -# the signer? See the "authenticationMethod" definition -# https://developers.docusign.com/esign-rest-api/reference/Envelopes/EnvelopeViews/createRecipient - - -@eg016.route("/eg016", methods=["POST"]) -@authenticate(eg=eg) -def set_tab_values(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Redirect the user to the embedded signing - """ - - # 1. Get required arguments - args = Eg016Controller.get_args() - - try: - # 2. Call the worker method for setting tab values - results = Eg016Controller.worker(args) - except ApiException as err: - return process_error(err) - - session["envelope_id"] = results["envelope_id"] # Save for use by other examples - # that need an envelope ID - # 3. Redirect the user to the embedded signing - # Don"t use an iframe! - # State can be stored/recovered using the framework"s session or a - # query parameter on the return URL (see the makeRecipientViewRequest method) - return redirect(results["redirect_url"]) - - -@eg016.route("/eg016", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - return render_template( - "eg016_set_tab_values.html", - title="SetTabValues", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"] - ) diff --git a/app/eSignature/examples/eg017_set_template_tab_values/controller.py b/app/eSignature/examples/eg017_set_template_tab_values.py similarity index 88% rename from app/eSignature/examples/eg017_set_template_tab_values/controller.py rename to app/eSignature/examples/eg017_set_template_tab_values.py index d7244bba..03253d92 100644 --- a/app/eSignature/examples/eg017_set_template_tab_values/controller.py +++ b/app/eSignature/examples/eg017_set_template_tab_values.py @@ -3,11 +3,11 @@ from flask import current_app as app, url_for, request from flask import session -from ....consts import authentication_method, signer_client_id, pattern -from ....docusign import create_api_client +from ...consts import authentication_method, signer_client_id, pattern +from ...docusign import create_api_client -class Eg017Controller: +class Eg017SetTemplateTabValuesController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -42,20 +42,28 @@ def worker(cls, args): 4. Obtain the recipient_view_url for the embedded signing """ envelope_args = args["envelope_args"] - # 1. Create the envelope request object + # Create the envelope request object + #ds-snippet-start:eSign17Step4 envelope_definition = cls.make_envelope(envelope_args) + #ds-snippet-end:eSign17Step4 - # 2. call Envelopes::create API method + # Call Envelopes::create API method # Exceptions will be caught by the calling function + + #ds-snippet-start:eSign17Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign17Step2 + #ds-snippet-start:eSign17Step5 envelopes_api = EnvelopesApi(api_client) results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) envelope_id = results.envelope_id + #ds-snippet-end:eSign17Step5 app.logger.info(f"Envelope was created. EnvelopeId {envelope_id}") - # 3. Create the Recipient View request object + # Create the Recipient View request object + #ds-snippet-start:eSign17Step6 recipient_view_request = RecipientViewRequest( authentication_method=authentication_method, client_user_id=envelope_args["signer_client_id"], @@ -63,7 +71,7 @@ def worker(cls, args): return_url=envelope_args["ds_return_url"], user_name=envelope_args["signer_name"], email=envelope_args["signer_email"] ) - # 4. Obtain the recipient_view_url for the embedded signing + # Obtain the recipient_view_url for the embedded signing # Exceptions will be caught by the calling function results = envelopes_api.create_recipient_view( account_id=args["account_id"], @@ -71,6 +79,7 @@ def worker(cls, args): recipient_view_request=recipient_view_request ) return {"envelope_id": envelope_id, "redirect_url": results.url} + #ds-snippet-end:eSign17Step6 @classmethod def make_envelope(cls, args): @@ -83,6 +92,7 @@ def make_envelope(cls, args): # Set the values for the fields in the template # List item + #ds-snippet-start:eSign17Step3 list1 = List( value="green", document_id="1", page_number="1", tab_label="list") @@ -161,3 +171,4 @@ def make_envelope(cls, args): ) return envelope_definition + #ds-snippet-end:eSign17Step3 diff --git a/app/eSignature/examples/eg017_set_template_tab_values/__init__.py b/app/eSignature/examples/eg017_set_template_tab_values/__init__.py deleted file mode 100644 index 5c5c41b3..00000000 --- a/app/eSignature/examples/eg017_set_template_tab_values/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg017 diff --git a/app/eSignature/examples/eg018_envelope_custom_field_data/controller.py b/app/eSignature/examples/eg018_envelope_custom_field_data.py similarity index 77% rename from app/eSignature/examples/eg018_envelope_custom_field_data/controller.py rename to app/eSignature/examples/eg018_envelope_custom_field_data.py index 9706b463..dd7b23c1 100644 --- a/app/eSignature/examples/eg018_envelope_custom_field_data/controller.py +++ b/app/eSignature/examples/eg018_envelope_custom_field_data.py @@ -1,10 +1,10 @@ from docusign_esign import EnvelopesApi from flask import session -from ....docusign import create_api_client +from ...docusign import create_api_client -class Eg018Controller: +class Eg018EnvelopeCustomFieldDataController: @staticmethod def get_args(): """Get required session arguments""" @@ -22,8 +22,11 @@ def worker(args): """ # Exceptions will be caught by the calling function + #ds-snippet-start:eSign18Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign18Step2 + #ds-snippet-start:eSign18Step3 envelopes_api = EnvelopesApi(api_client) results = envelopes_api.list_custom_fields(account_id=args["account_id"], envelope_id=args["envelope_id"]) - + #ds-snippet-end:eSign18Step3 return results diff --git a/app/eSignature/examples/eg018_envelope_custom_field_data/__init__.py b/app/eSignature/examples/eg018_envelope_custom_field_data/__init__.py deleted file mode 100644 index 96299bf3..00000000 --- a/app/eSignature/examples/eg018_envelope_custom_field_data/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg018 diff --git a/app/eSignature/examples/eg018_envelope_custom_field_data/views.py b/app/eSignature/examples/eg018_envelope_custom_field_data/views.py deleted file mode 100644 index c406e974..00000000 --- a/app/eSignature/examples/eg018_envelope_custom_field_data/views.py +++ /dev/null @@ -1,69 +0,0 @@ -"""018: Get an envelope"s custom field data""" - -import json -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, session, Blueprint - -from .controller import Eg018Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg018" # reference (and URL) for this example -eg018 = Blueprint("eg018", __name__) - - -@eg018.route("/eg018", methods=["POST"]) -@authenticate(eg=eg) -def envelope_custom_field_data(): - """ - 1. Get required args - 2. Call the worker method - 3. Show custom field data - """ - - if "envelope_id" in session: - # 1. Get required args - args = Eg018Controller.get_args() - - try: - # 2. Call the worker method for creating envelope with custom field - results = Eg018Controller.worker(args) - except ApiException as err: - return process_error(err) - # 3. Step render the results - return render_template( - "example_done.html", - title="Get custom field data", - h1="Envelope custom field data", - message="Results from the EnvelopeCustomFields::list method:", - json=json.dumps(json.dumps(results.to_dict())) - ) - else: - return render_template( - "eg018_envelope_custom_field_data.html", - title="Envelope Custom Field Data", - envelope_ok=False, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) - - -@eg018.route("/eg018", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - return render_template( - "eg015_envelope_tab_data.html", - title="Envelope information", - envelope_ok="envelope_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) diff --git a/app/eSignature/examples/eg019_access_code_authentication/controller.py b/app/eSignature/examples/eg019_access_code_authentication.py similarity index 87% rename from app/eSignature/examples/eg019_access_code_authentication/controller.py rename to app/eSignature/examples/eg019_access_code_authentication.py index da0b7571..2bda31f4 100644 --- a/app/eSignature/examples/eg019_access_code_authentication/controller.py +++ b/app/eSignature/examples/eg019_access_code_authentication.py @@ -4,12 +4,12 @@ from docusign_esign import EnvelopesApi, EnvelopeDefinition, Document, Signer, SignHere, Tabs, Recipients from flask import session, request -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG -class Eg019Controller: +class Eg019AccessCodeAuthenticationController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -39,10 +39,13 @@ def worker(args): 2. Create an envelope definition object 3. Call the eSignature REST API using the SDK """ - # Step 1: Construct your API headers + # Construct your API headers + #ds-snippet-start:eSign19Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign19Step2 - # Step 2: Construct your envelope + # Construct your envelope + #ds-snippet-start:eSign19Step3 envelope_definition = EnvelopeDefinition( email_subject="Please sign this document set" ) @@ -91,8 +94,11 @@ def worker(args): # Tabs are set per recipient envelope_definition.recipients = Recipients(signers=[signer1]) - # Step 3: Call the eSignature REST API + #ds-snippet-end:eSign19Step3 + + # Call the eSignature REST API + #ds-snippet-start:eSign19Step4 envelope_api = EnvelopesApi(api_client) results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) - + #ds-snippet-end:eSign19Step4 return results diff --git a/app/eSignature/examples/eg019_access_code_authentication/__init__.py b/app/eSignature/examples/eg019_access_code_authentication/__init__.py deleted file mode 100644 index 7b48ff8c..00000000 --- a/app/eSignature/examples/eg019_access_code_authentication/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg019 diff --git a/app/eSignature/examples/eg019_access_code_authentication/views.py b/app/eSignature/examples/eg019_access_code_authentication/views.py deleted file mode 100644 index b33634c6..00000000 --- a/app/eSignature/examples/eg019_access_code_authentication/views.py +++ /dev/null @@ -1,58 +0,0 @@ -""" Example 019: Access Code Recipient Authentication""" - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import current_app as app -from flask import render_template, Blueprint - -from .controller import Eg019Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg019" # reference (and url) for this example -eg019 = Blueprint("eg019", __name__) - -@eg019.route("/eg019", methods=["POST"]) -@authenticate(eg=eg) -def access_code_authentication(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render success response - """ - args = Eg019Controller.get_args() - try: - # Step 1: Call the worker method for authenticating with access code - results = Eg019Controller.worker(args) - envelope_id = results.envelope_id - - app.logger.info(f"Envelope was created. EnvelopeId {envelope_id} ") - - return render_template( - "example_done.html", - title="Envelope sent", - h1="Envelope sent", - message=f"""The envelope has been created and sent!
Envelope ID {envelope_id}.""" - ) - - except ApiException as err: - return process_error(err) - - -@eg019.route("/eg019", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - return render_template( - "eg019_access_code_authentication.html", - title="Access-code recipient authentication", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"] - ) diff --git a/app/eSignature/examples/eg020_phone_authentication.py b/app/eSignature/examples/eg020_phone_authentication.py new file mode 100644 index 00000000..5e2af839 --- /dev/null +++ b/app/eSignature/examples/eg020_phone_authentication.py @@ -0,0 +1,136 @@ +import base64 +from os import path + +from docusign_esign import AccountsApi, EnvelopesApi, EnvelopeDefinition, Document, Signer, SignHere, Tabs, Recipients, RecipientIdentityInputOption, RecipientIdentityPhoneNumber +from docusign_esign.client.api_exception import ApiException +from flask import current_app as app, session, request + +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error + + +class Eg020PhoneAuthenticationController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + # More data validation would be a good idea here + # Strip anything other than the characters listed + signer_email = pattern.sub("", request.form.get("signer_email")) + signer_name = pattern.sub("", request.form.get("signer_name")) + country_code = pattern.sub("", request.form.get("country_code")) + phone_number = pattern.sub("", request.form.get("phone_number")) + envelope_args = { + "signer_email": signer_email, + "signer_name": signer_name, + "phone_number": phone_number, + "country_code": country_code, + "status": "sent", + "workflow_id": session['workflow_id'] + } + args = { + "account_id": session["ds_account_id"], # represents your {ACCOUNT_ID} + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], # represnts your {ACCESS_TOKEN} + "envelope_args": envelope_args + } + return args + + @staticmethod + def worker(args): + """ + 1. Create an api client + 2. Create an envelope definition object + """ + # Construct your API headers + #ds-snippet-start:eSign20Step2 + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign20Step2 + + # Construct your envelope + #ds-snippet-start:eSign20Step4 + envelope_definition = EnvelopeDefinition( + email_subject="Please sign this document set" + ) + + # Open the example file + with open(path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), "rb") as file: + content_bytes = file.read() + base64_file_content = base64.b64encode(content_bytes).decode("ascii") + + # Add a document + document1 = Document( # create the DocuSign document object + document_base64=base64_file_content, + document_id="1", # a label used to reference the doc + file_extension="pdf", # many different document types are accepted + name="Lorem" # can be different from actual file name + ) + + envelope_definition.documents = [document1] + envelope_definition.status = args["envelope_args"]["status"] + + # Create your signature tab + sign_here1 = SignHere( + name="SignHereTab", + anchor_string="/sn1/", + anchor_units="pixels", + anchor_y_offset="10", + anchor_x_offset="20", + tab_label="SignHereTab", + page_number="1", + document_id="1", + # A 1- to 8-digit integer or 32-character GUID to match recipient IDs on your own systems. + # This value is referenced in the Tabs element below to assign tabs on a per-recipient basis. + recipient_id="1" # represents your {RECIPIENT_ID} + ) + + signer1 = Signer( + email=args["envelope_args"]["signer_email"], # Represents your {signer_email} + name=args["envelope_args"]["signer_name"], # Represents your {signer_name} + role_name="", + note="", + status="created", + delivery_method="email", + recipient_id="1", # Represents your {RECIPIENT_ID} + routing_order="1", + identity_verification={ "workflowId": session['workflow_id'], "steps": "null", "inputOptions":[{"name":"phone_number_list","valueType":"PhoneNumberList","phoneNumberList":[{"countryCode":args["envelope_args"]["country_code"],"code":"1","number":args["envelope_args"]["phone_number"]}]}], "idCheckConfigurationName":""}, + tabs=Tabs(sign_here_tabs=[sign_here1]) + ) + + # Tabs are set per recipient + envelope_definition.recipients = Recipients(signers=[signer1]) + #ds-snippet-end:eSign20Step4 + + # Call the eSignature REST API + #ds-snippet-start:eSign20Step5 + envelopes_api = EnvelopesApi(api_client) + results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + #ds-snippet-end:eSign20Step5 + + return results + + @staticmethod + def get_workflow(args): + """Retrieve the workflow id""" + try: + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-start:eSign20Step3 + workflow_details = AccountsApi(api_client) + workflow_response = workflow_details.get_account_identity_verification(account_id=args["account_id"]) + + # Check that idv authentication is enabled + # Find the workflow ID corresponding to the name "Phone Authentication" + if workflow_response.identity_verification: + for workflow in workflow_response.identity_verification: + if workflow.default_name == "Phone Authentication": + session['workflow_id'] = workflow.workflow_id + #ds-snippet-end:eSign20Step3 + return session['workflow_id'] + + else: + return None + + except ApiException as err: + return process_error(err) + diff --git a/app/eSignature/examples/eg020_sms_authentication/__init__.py b/app/eSignature/examples/eg020_sms_authentication/__init__.py deleted file mode 100644 index f06e8874..00000000 --- a/app/eSignature/examples/eg020_sms_authentication/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg020 diff --git a/app/eSignature/examples/eg020_sms_authentication/controller.py b/app/eSignature/examples/eg020_sms_authentication/controller.py deleted file mode 100644 index 3b132a75..00000000 --- a/app/eSignature/examples/eg020_sms_authentication/controller.py +++ /dev/null @@ -1,100 +0,0 @@ -import base64 -from os import path - -from docusign_esign import EnvelopesApi, EnvelopeDefinition, Document, Signer, SignHere, Tabs, Recipients -from flask import request, session - -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG - - -class Eg020Controller: - @staticmethod - def get_args(): - """Get required session and request arguments""" - # More data validation would be a good idea here - # Strip anything other than characters listed - phone_number = request.form.get("phone_number") - signer_email = pattern.sub("", request.form.get("signer_email")) - signer_name = pattern.sub("", request.form.get("signer_name")) - envelope_args = { - "signer_email": signer_email, - "signer_name": signer_name, - "status": "sent", - "phone_number": phone_number - } - - args = { - "account_id": session["ds_account_id"], # represents your {ACCOUNT_ID} - "base_path": session["ds_base_path"], - "access_token": session["ds_access_token"], # represnts your {ACCESS_TOKEN} - "envelope_args": envelope_args - } - return args - - @staticmethod - def worker(args): - """ - 1. Create an api client - 2. Create an envelope definition object - 3. Call the eSignature REST API using the SDK - """ - # Step 1: Construct your API headers - api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) - - # Step 2: Construct your envelope - envelope_definition = EnvelopeDefinition( - email_subject="Please sign this document set" - ) - - # Open the example file - with open(path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), "rb") as file: - content_bytes = file.read() - base64_file_content = base64.b64encode(content_bytes).decode("ascii") - - # Add a Document - document1 = Document( # create the DocuSign document object - document_base64=base64_file_content, - document_id="1", # a label used to reference the doc - file_extension="pdf", # many different document types are accepted - name="Lorem" # can be different from actual file name - ) - - envelope_definition.documents = [document1] - envelope_definition.status = args["envelope_args"]["status"] - - signer1 = Signer( - email=args["envelope_args"]["signer_email"], # represents your {signer_email} - name=args["envelope_args"]["signer_name"], # represents your {signer_name} - sms_authentication={"senderProvidedNumbers": [args["envelope_args"]["phone_number"]]}, - id_check_configuration_name="SMS Auth $", - require_id_lookup="true", - recipient_id="1", - routing_order="1" - ) - - # Create your signature tab - sign_here1 = SignHere( - name="SignHereTab", - x_position="75", - y_position="572", - tab_label="SignHereTab", - page_number="1", - document_id="1", - # A 1- to 8-digit integer or 32-character GUID to match recipient IDs on your own systems. - # This value is referenced in the Tabs element below to assign tabs on a per-recipient basis. - recipient_id="1" # represents your {RECIPIENT_ID} - ) - - # Add the tabs model (including the sign_here tabs) to the signer - # The Tabs object wants arrays of the different field/tab types - signer1.tabs = Tabs(sign_here_tabs=[sign_here1]) - - # Tabs are set per recipient - envelope_definition.recipients = Recipients(signers=[signer1]) - # Step 3: Call the eSignature REST API - envelope_api = EnvelopesApi(api_client) - results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) - - return results diff --git a/app/eSignature/examples/eg020_sms_authentication/views.py b/app/eSignature/examples/eg020_sms_authentication/views.py deleted file mode 100644 index 5e28e662..00000000 --- a/app/eSignature/examples/eg020_sms_authentication/views.py +++ /dev/null @@ -1,61 +0,0 @@ -""" Example 020: Sms Recipient Authentication""" - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import current_app as app -from flask import render_template, Blueprint - -from .controller import Eg020Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg020" # reference (and url) for this example -eg020 = Blueprint("eg020", __name__) - - -@eg020.route("/eg020", methods=["POST"]) -@authenticate(eg=eg) -def sms_authentication(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render success response - """ - - # 1. Get required arguments - args = Eg020Controller.get_args() - try: - # Step 2: Call the worker method for sms authenticating - results = Eg020Controller.worker(args) - envelope_id = results.envelope_id - app.logger.info(f"Envelope was created. EnvelopeId {envelope_id} ") - - # 3. Render success response - return render_template( - "example_done.html", - title="Envelope sent", - h1="Envelope sent", - message=f"""The envelope has been created and sent!
Envelope ID {envelope_id}.""" - ) - - except ApiException as err: - return process_error(err) - - -@eg020.route("/eg020", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - return render_template( - "eg020_sms_authentication.html", - title="SMS recipient authentication", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"] - ) diff --git a/app/eSignature/examples/eg021_phone_authentication/__init__.py b/app/eSignature/examples/eg021_phone_authentication/__init__.py deleted file mode 100644 index 88a2b913..00000000 --- a/app/eSignature/examples/eg021_phone_authentication/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg021 diff --git a/app/eSignature/examples/eg021_phone_authentication/controller.py b/app/eSignature/examples/eg021_phone_authentication/controller.py deleted file mode 100644 index 3ba76e1e..00000000 --- a/app/eSignature/examples/eg021_phone_authentication/controller.py +++ /dev/null @@ -1,99 +0,0 @@ -import base64 -from os import path - -from docusign_esign import EnvelopesApi, EnvelopeDefinition, Document, Signer, SignHere, Tabs, Recipients -from flask import session, request - -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG - - -class Eg021Controller: - @staticmethod - def get_args(): - """Get required session and request arguments""" - # More data validation would be a good idea here - # Strip anything other than characters listed - phone_number = request.form.get("phoneNumber") - signer_email = pattern.sub("", request.form.get("signer_email")) - signer_name = pattern.sub("", request.form.get("signer_name")) - envelope_args = { - "signer_email": signer_email, - "signer_name": signer_name, - "status": "sent", - "phone_number": phone_number - } - args = { - "account_id": session["ds_account_id"], # represents your {ACCOUNT_ID} - "base_path": session["ds_base_path"], - "access_token": session["ds_access_token"], # represnts your {ACCESS_TOKEN} - "envelope_args": envelope_args - } - return args - - @staticmethod - def worker(args): - """ - 1. Create an api client - 2. Create an envelope definition object - 3. Call the eSignature REST API using the SDK - """ - # Step 1: Construct your API headers - api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) - - # Step 2: Construct your envelope - envelope_definition = EnvelopeDefinition( - email_subject="Please sign this document set" - ) - - # Open the example file - with open(path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), "rb") as file: - content_bytes = file.read() - base64_file_content = base64.b64encode(content_bytes).decode("ascii") - - # Add a Document - document1 = Document( # create the DocuSign document object - document_base64=base64_file_content, - document_id="1", # a label used to reference the doc - file_extension="pdf", # many different document types are accepted - name="Lorem" # can be different from actual file name - ) - - envelope_definition.documents = [document1] - envelope_definition.status = args["envelope_args"]["status"] - - signer1 = Signer( - email=args["envelope_args"]["signer_email"], # represents your {signer_email} - name=args["envelope_args"]["signer_name"], # represents your {signer_name} - phone_authentication={"senderProvidedNumbers": [args["envelope_args"]["phone_number"]]}, - id_check_configuration_name="Phone Auth $", - require_id_lookup="true", - recipient_id="1", - routing_order="1" - ) - - # Create your signature tab - sign_here1 = SignHere( - name="SignHereTab", - x_position="75", - y_position="572", - tab_label="SignHereTab", - page_number="1", - document_id="1", - # A 1- to 8-digit integer or 32-character GUID to match recipient IDs on your own systems. - # This value is referenced in the Tabs element below to assign tabs on a per-recipient basis. - recipient_id="1" # represents your {RECIPIENT_ID} - ) - - # Add the tabs model (including the sign_here tabs) to the signer - # The Tabs object wants arrays of the different field/tab types - signer1.tabs = Tabs(sign_here_tabs=[sign_here1]) - - # Tabs are set per recipient - envelope_definition.recipients = Recipients(signers=[signer1]) - # Step 3: Call the eSignature REST API - envelope_api = EnvelopesApi(api_client) - results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) - - return results diff --git a/app/eSignature/examples/eg021_phone_authentication/views.py b/app/eSignature/examples/eg021_phone_authentication/views.py deleted file mode 100644 index cc91e922..00000000 --- a/app/eSignature/examples/eg021_phone_authentication/views.py +++ /dev/null @@ -1,61 +0,0 @@ -""" Example 021: Recipient Phone Authentication""" - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import current_app as app -from flask import render_template, Blueprint - -from .controller import Eg021Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg021" # reference (and url) for this example -eg021 = Blueprint("eg021", __name__) - - -@eg021.route("/eg021", methods=["POST"]) -@authenticate(eg=eg) -def phone_authentication(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render success response - """ - - # 1. Get required arguments - args = Eg021Controller.get_args() - try: - # Step 2: Call the worker method for authenticating with phone - results = Eg021Controller.worker(args) - envelope_id = results.envelope_id - app.logger.info(f"Envelope was created. EnvelopeId {envelope_id} ") - - # 3. Render success response - return render_template( - "example_done.html", - title="Envelope sent", - h1="Envelope sent", - message=f"""The envelope has been created and sent!
Envelope ID {envelope_id}.""" - ) - - except ApiException as err: - return process_error(err) - - -@eg021.route("/eg021", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - return render_template( - "eg021_phone_authentication.html", - title="Phone recipient authentication", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"] - ) diff --git a/app/eSignature/examples/eg022_kba_authentication/controller.py b/app/eSignature/examples/eg022_kba_authentication.py similarity index 87% rename from app/eSignature/examples/eg022_kba_authentication/controller.py rename to app/eSignature/examples/eg022_kba_authentication.py index 6b7033fc..29bc68e5 100644 --- a/app/eSignature/examples/eg022_kba_authentication/controller.py +++ b/app/eSignature/examples/eg022_kba_authentication.py @@ -4,12 +4,12 @@ from docusign_esign import EnvelopesApi, EnvelopeDefinition, Document, Signer, SignHere, Tabs, Recipients from flask import session, request -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG -class Eg022Controller: +class Eg022KBAAuthenticationController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -37,10 +37,13 @@ def worker(args): 2. Create an envelope definition object 3. Call the eSignature REST API using the SDK """ - # Step 1: Construct your API headers + # Construct your API headers + #ds-snippet-start:eSign22Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign22Step2 - # Step 2: Construct your envelope + # Construct your envelope + #ds-snippet-start:eSign22Step3 envelope_definition = EnvelopeDefinition( email_subject="Please sign this document set" ) @@ -89,8 +92,11 @@ def worker(args): # Tabs are set per recipient envelope_definition.recipients = Recipients(signers=[signer1]) - # Step 3: Call the eSignature REST API + #ds-snippet-end:eSign22Step3 + + # Call the eSignature REST API + #ds-snippet-start:eSign22Step4 envelope_api = EnvelopesApi(api_client) results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) - + #ds-snippet-end:eSign22Step4 return results diff --git a/app/eSignature/examples/eg022_kba_authentication/__init__.py b/app/eSignature/examples/eg022_kba_authentication/__init__.py deleted file mode 100644 index 3c07b6dc..00000000 --- a/app/eSignature/examples/eg022_kba_authentication/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg022 diff --git a/app/eSignature/examples/eg022_kba_authentication/views.py b/app/eSignature/examples/eg022_kba_authentication/views.py deleted file mode 100644 index 10e3f5bd..00000000 --- a/app/eSignature/examples/eg022_kba_authentication/views.py +++ /dev/null @@ -1,60 +0,0 @@ -""" Example 022: Knowledge Based authentication""" - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import current_app as app -from flask import render_template, Blueprint - -from .controller import Eg022Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg022" # reference (and url) for this example -eg022 = Blueprint("eg022", __name__) - - -@eg022.route("/eg022", methods=["POST"]) -@authenticate(eg=eg) -def kba_authentication(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render success response - """ - - # 1. Get required arguments - args = Eg022Controller.get_args() - try: - # Step 2: Call the worker method for kba - results = Eg022Controller.worker(args) - envelope_id = results.envelope_id - app.logger.info(f"Envelope was created. EnvelopeId {envelope_id} ") - - # 3. Render success response - return render_template( - "example_done.html", - title="Envelope sent", - h1="Envelope sent", - message=f"""The envelope has been created and sent!
Envelope ID {envelope_id}.""" - ) - - except ApiException as err: - return process_error(err) - - -@eg022.route("/eg022", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - return render_template( - "eg022_kba_authentication.html", - title="Kba recipient authentication", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"] - ) diff --git a/app/eSignature/examples/eg023_idv_authentication/controller.py b/app/eSignature/examples/eg023_idv_authentication.py similarity index 75% rename from app/eSignature/examples/eg023_idv_authentication/controller.py rename to app/eSignature/examples/eg023_idv_authentication.py index b77ff720..26390923 100644 --- a/app/eSignature/examples/eg023_idv_authentication/controller.py +++ b/app/eSignature/examples/eg023_idv_authentication.py @@ -5,13 +5,13 @@ from docusign_esign.client.api_exception import ApiException from flask import current_app as app, session, request -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error -class Eg023Controller: +class Eg023IDVAuthenticationController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -39,10 +39,13 @@ def worker(args): 1. Create an api client 2. Create an envelope definition object """ - # Step 1: Construct your API headers + # Construct your API headers + #ds-snippet-start:eSign23Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign23Step2 - # Step 3: Construct your envelope + # Construct your envelope + #ds-snippet-start:eSign23Step4 envelope_definition = EnvelopeDefinition( email_subject="Please sign this document set" ) @@ -66,8 +69,8 @@ def worker(args): # Create your signature tab sign_here1 = SignHere( name="SignHereTab", - x_position="75", - y_position="572", + x_position="200", + y_position="160", tab_label="SignHereTab", page_number="1", document_id="1", @@ -91,11 +94,13 @@ def worker(args): # Tabs are set per recipient envelope_definition.recipients = Recipients(signers=[signer1]) + #ds-snippet-end:eSign23Step4 - # Step 4: Call the eSignature REST API + # Call the eSignature REST API + #ds-snippet-start:eSign23Step5 envelopes_api = EnvelopesApi(api_client) results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) - + #ds-snippet-end:eSign23Step5 return results @staticmethod @@ -104,19 +109,31 @@ def get_workflow(args): try: api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-start:eSign23Step3 workflow_details = AccountsApi(api_client) workflow_response = workflow_details.get_account_identity_verification(account_id=args["account_id"]) # Check that idv authentication is enabled if workflow_response.identity_verification: - workflow_id = workflow_response.identity_verification[0].workflow_id - app.logger.info("We found the following workflowID: " + workflow_id) + # Find the workflow ID corresponding to the name "DocuSign ID Verification" + workflow_id = None + + for workflow in workflow_response.identity_verification: + if workflow.default_name == "DocuSign ID Verification": + workflow_id = workflow.workflow_id + break + + if workflow_id is not None: + app.logger.info("We found the following workflowID: " + workflow_id) + else: + app.logger.info("No workflowID was found for DocuSign ID Verification.") + session['workflow_id'] = workflow_id - return workflow_id else: return None + #ds-snippet-end:eSign23Step3 except ApiException as err: return process_error(err) diff --git a/app/eSignature/examples/eg023_idv_authentication/__init__.py b/app/eSignature/examples/eg023_idv_authentication/__init__.py deleted file mode 100644 index b66f3e80..00000000 --- a/app/eSignature/examples/eg023_idv_authentication/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg023 diff --git a/app/eSignature/examples/eg023_idv_authentication/views.py b/app/eSignature/examples/eg023_idv_authentication/views.py deleted file mode 100644 index 75e3689c..00000000 --- a/app/eSignature/examples/eg023_idv_authentication/views.py +++ /dev/null @@ -1,69 +0,0 @@ -""" Example 023: ID Verificiation Based authentication""" - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import current_app as app -from flask import render_template, Blueprint, session - -from .controller import Eg023Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg023" # Reference (and URL) for this example -eg023 = Blueprint("eg023", __name__) - - -@eg023.route("/eg023", methods=["POST"]) -@authenticate(eg=eg) -def idv_authentication(): - """ - 1. Get required data - 2. Call the worker method - 3. Render success response - """ - # 1. Get required data - args = Eg023Controller.get_args() - try: - # 2: Call the worker method for idv authentication - results = Eg023Controller.worker(args) - envelope_id = results.envelope_id - app.logger.info(f"Envelope was created. EnvelopeId {envelope_id} ") - - # 3. Render success response - return render_template( - "example_done.html", - title="Envelope sent", - h1="Envelope sent", - message=f"The envelope has been created and sent!
Envelope ID {envelope_id}." - ) - - except ApiException as err: - return process_error(err) - - -@eg023.route("/eg023", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - args = { - "account_id": session["ds_account_id"], # represent your {ACCOUNT_ID} - "base_path": session["ds_base_path"], - "access_token": session["ds_access_token"], # represent your {ACCESS_TOKEN} - - } - - workflow_id = Eg023Controller.get_workflow(args) - - return render_template( - "eg023_idv_authentication.html", - title="IDV authentication", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"], - workflow_id=workflow_id - ) diff --git a/app/eSignature/examples/eg024_permissions_creating/controller.py b/app/eSignature/examples/eg024_permissions_creating.py similarity index 73% rename from app/eSignature/examples/eg024_permissions_creating/controller.py rename to app/eSignature/examples/eg024_permissions_creating.py index b0c0d0f1..61296042 100644 --- a/app/eSignature/examples/eg024_permissions_creating/controller.py +++ b/app/eSignature/examples/eg024_permissions_creating.py @@ -1,11 +1,11 @@ from docusign_esign import AccountsApi, PermissionProfile from flask import session, request -from ....consts import settings -from ....docusign import create_api_client +from ...consts import settings +from ...docusign import create_api_client -class Eg024Controller: +class Eg024PermissionsCreatingController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -26,20 +26,26 @@ def worker(args): 3. Create the permission profile using the SDK """ - # Step 2. Construct your API request headers + # Construct your API request headers + #ds-snippet-start:eSign24Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign24Step2 - # Step 3. Construct your request body + # Construct your request body + #ds-snippet-start:eSign24Step3 permission_profile = PermissionProfile( permission_profile_name=args["permission_profile_name"], settings=args["settings"] ) + #ds-snippet-end:eSign24Step3 - # Step 4. Call the eSignature REST API + # Call the eSignature REST API + #ds-snippet-start:eSign24Step4 account_api = AccountsApi(api_client) response = account_api.create_permission_profile( account_id=args["account_id"], permission_profile=permission_profile ) + #ds-snippet-end:eSign24Step4 return response diff --git a/app/eSignature/examples/eg024_permissions_creating/__init__.py b/app/eSignature/examples/eg024_permissions_creating/__init__.py deleted file mode 100644 index 4511f0d1..00000000 --- a/app/eSignature/examples/eg024_permissions_creating/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg024 \ No newline at end of file diff --git a/app/eSignature/examples/eg024_permissions_creating/views.py b/app/eSignature/examples/eg024_permissions_creating/views.py deleted file mode 100644 index c9302dc5..00000000 --- a/app/eSignature/examples/eg024_permissions_creating/views.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Example 024: Creating a permission profile""" - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import current_app as app -from flask import render_template, Blueprint - -from .controller import Eg024Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg024" -eg024 = Blueprint("eg024", __name__) - - -@eg024.route("/eg024", methods=["POST"]) -@authenticate(eg=eg) -def permissions_creating(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render a response - """ - - # 1. Get required args - args = Eg024Controller.get_args() - try: - # 2. Call the worker method to create a permission profile - response = Eg024Controller.worker(args) - permission_profile_id = response.permission_profile_id - app.logger.info(f"The permission profile has been created. Permission profile ID: {permission_profile_id}") - - # 3. Render the response - return render_template( - "example_done.html", - title="Creating a permission profile", - h1="Creating a permission profile", - message=f"""The permission profile has been created!
Permission profile ID: {permission_profile_id}.""" - ) - - except ApiException as err: - return process_error(err) - -@eg024.route("/eg024", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - return render_template( - "eg024_permissions_creating.html", - title="Creating a permission profile", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - permission_profile_name="Sample Profile 134972-Alpha" - ) \ No newline at end of file diff --git a/app/eSignature/examples/eg025_permissions_set_user_group/controller.py b/app/eSignature/examples/eg025_permissions_set_user_group.py similarity index 79% rename from app/eSignature/examples/eg025_permissions_set_user_group/controller.py rename to app/eSignature/examples/eg025_permissions_set_user_group.py index fa2b8a69..e3168137 100644 --- a/app/eSignature/examples/eg025_permissions_set_user_group/controller.py +++ b/app/eSignature/examples/eg025_permissions_set_user_group.py @@ -1,10 +1,10 @@ from docusign_esign import AccountsApi, Group, GroupInformation, GroupsApi from docusign_esign.client.api_exception import ApiException from flask import session, request -from ....docusign import create_api_client -from ....error_handlers import process_error +from ...docusign import create_api_client +from ...error_handlers import process_error -class Eg025Controller: +class Eg025PermissionsSetUserGroupController: @staticmethod def get_args(): return { @@ -24,17 +24,22 @@ def worker(args): Step 4: Update the group """ - # Step 2. Construct your API headers + # Construct your API headers + #ds-snippet-start:eSign25Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) group_api = GroupsApi(api_client) + #ds-snippet-end:eSign25Step2 - # Step 3. Construct your request body + # Construct your request body + #ds-snippet-start:eSign25Step3 group = Group(group_id=args["group_id"], permission_profile_id=args["permission_profile_id"]) group_information = GroupInformation(groups=[group]) + #ds-snippet-end:eSign25Step3 - # Step 4. Call the eSignature REST API + # Call the eSignature REST API + #ds-snippet-start:eSign25Step4 response = group_api.update_groups(account_id=args["account_id"], group_information=group_information) - + #ds-snippet-end:eSign25Step4 return response @staticmethod diff --git a/app/eSignature/examples/eg025_permissions_set_user_group/__init__.py b/app/eSignature/examples/eg025_permissions_set_user_group/__init__.py deleted file mode 100644 index eab4fbef..00000000 --- a/app/eSignature/examples/eg025_permissions_set_user_group/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg025 \ No newline at end of file diff --git a/app/eSignature/examples/eg026_permissions_change_single_setting/controller.py b/app/eSignature/examples/eg026_permissions_change_single_setting.py similarity index 86% rename from app/eSignature/examples/eg026_permissions_change_single_setting/controller.py rename to app/eSignature/examples/eg026_permissions_change_single_setting.py index 80cbadb9..49b2a76b 100644 --- a/app/eSignature/examples/eg026_permissions_change_single_setting/controller.py +++ b/app/eSignature/examples/eg026_permissions_change_single_setting.py @@ -1,11 +1,11 @@ from docusign_esign import AccountsApi, PermissionProfile from docusign_esign.client.api_exception import ApiException from flask import request, session -from ....consts import settings -from ....docusign import create_api_client -from ....error_handlers import process_error +from ...consts import settings +from ...docusign import create_api_client +from ...error_handlers import process_error -class Eg026Controller: +class Eg026PermissionsChangeSingleSettingController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -29,9 +29,12 @@ def worker(args): """ # Step 2. Construct your API headers + #ds-snippet-start:eSign26Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign26Step2 # Step 3. Construct your request body + #ds-snippet-start:eSign26Step3 permission_profile = PermissionProfile( settings=args["settings"] ) @@ -40,13 +43,16 @@ def worker(args): account_id=args["account_id"], permission_profile_id=args["permission_profile_id"] ).settings.to_dict() + #ds-snippet-end:eSign26Step3 # Step 4. Call the eSignature REST API + #ds-snippet-start:eSign26Step4 response = account_api.update_permission_profile( account_id=args["account_id"], permission_profile_id=args["permission_profile_id"], permission_profile=permission_profile ) new_settings = response.settings.to_dict() + #ds-snippet-end:eSign26Step4 changed_settings = {} # Save only changed settings diff --git a/app/eSignature/examples/eg026_permissions_change_single_setting/__init__.py b/app/eSignature/examples/eg026_permissions_change_single_setting/__init__.py deleted file mode 100644 index 7b085b79..00000000 --- a/app/eSignature/examples/eg026_permissions_change_single_setting/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg026 \ No newline at end of file diff --git a/app/eSignature/examples/eg027_permissions_delete/controller.py b/app/eSignature/examples/eg027_permissions_delete.py similarity index 83% rename from app/eSignature/examples/eg027_permissions_delete/controller.py rename to app/eSignature/examples/eg027_permissions_delete.py index 90c10673..879eb84f 100644 --- a/app/eSignature/examples/eg027_permissions_delete/controller.py +++ b/app/eSignature/examples/eg027_permissions_delete.py @@ -1,10 +1,10 @@ from docusign_esign import AccountsApi from docusign_esign.client.api_exception import ApiException from flask import session, request -from ....docusign import create_api_client -from ....error_handlers import process_error +from ...docusign import create_api_client +from ...error_handlers import process_error -class Eg027Controller: +class Eg027PermissionsDeleteController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -23,13 +23,17 @@ def worker(args): """ # Step 2. Construct your API headers + #ds-snippet-start:eSign27Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) account_api = AccountsApi(api_client) + #ds-snippet-end:eSign27Step2 # Step 3. Call the eSignature REST API + #ds-snippet-start:eSign27Step3 account_api.delete_permission_profile( account_id=args["account_id"], permission_profile_id=args["permission_profile_id"]) + #ds-snippet-end:eSign27Step3 @staticmethod def get_permissions_profiles(args): @@ -43,4 +47,4 @@ def get_permissions_profiles(args): return response.permission_profiles except ApiException as err: - return process_error(err) \ No newline at end of file + return process_error(err) diff --git a/app/eSignature/examples/eg027_permissions_delete/__init__.py b/app/eSignature/examples/eg027_permissions_delete/__init__.py deleted file mode 100644 index 0f01ca1c..00000000 --- a/app/eSignature/examples/eg027_permissions_delete/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg027 \ No newline at end of file diff --git a/app/eSignature/examples/eg027_permissions_delete/views.py b/app/eSignature/examples/eg027_permissions_delete/views.py deleted file mode 100644 index 6a27d11c..00000000 --- a/app/eSignature/examples/eg027_permissions_delete/views.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Example 027: Deleting a permission profile""" - -from os import path -from docusign_esign.client.api_exception import ApiException -from flask import current_app as app -from flask import render_template, session, request, Blueprint -from .controller import Eg027Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg027" -eg027 = Blueprint("eg027", __name__) - -@eg027.route("/eg027", methods=["POST"]) -@authenticate(eg=eg) -def permissions_delete(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render success response - """ - - args = Eg027Controller.get_args() - try: - # 2. Call the worker method to delete a permission profile - Eg027Controller.worker(args) - app.logger.info(f"The permission profile has been deleted.") - - # 3. Render success response - return render_template( - "example_done.html", - title="Deleting a permission profile", - h1="Deleting a permission profile", - message=f"The permission profile has been deleted!
" - ) - - except ApiException as err: - return process_error(err) - -@eg027.route("/eg027", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - args = { - "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} - "base_path": session["ds_base_path"], - "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} - } - permission_profiles = Eg027Controller.get_permissions_profiles(args) - return render_template( - "eg027_permissions_delete.html", - title="Deleting a permission profile", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - permission_profiles=permission_profiles - ) \ No newline at end of file diff --git a/app/eSignature/examples/eg028_brand_creating/controller.py b/app/eSignature/examples/eg028_brand_creating.py similarity index 80% rename from app/eSignature/examples/eg028_brand_creating/controller.py rename to app/eSignature/examples/eg028_brand_creating.py index da96f4a3..4be80509 100644 --- a/app/eSignature/examples/eg028_brand_creating/controller.py +++ b/app/eSignature/examples/eg028_brand_creating.py @@ -1,8 +1,8 @@ from docusign_esign import AccountsApi, Brand from flask import session, request -from ....docusign import create_api_client +from ...docusign import create_api_client -class Eg028Controller: +class Eg028BrandCreatingController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -23,15 +23,21 @@ def worker(args): """ # Step 2. Construct your API headers + #ds-snippet-start:eSign28Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign28Step2 # Step 3. Construct your request body + #ds-snippet-start:eSign28Step3 brand = Brand( brand_name=args["brand_name"], default_brand_language=args["default_language"], ) + #ds-snippet-end:eSign28Step3 # Step 4. Call the eSignature REST API + #ds-snippet-start:eSign28Step4 account_api = AccountsApi(api_client) response = account_api.create_brand(account_id=args["account_id"], brand=brand) + #ds-snippet-end:eSign28Step4 return response \ No newline at end of file diff --git a/app/eSignature/examples/eg028_brand_creating/__init__.py b/app/eSignature/examples/eg028_brand_creating/__init__.py deleted file mode 100644 index 41007bea..00000000 --- a/app/eSignature/examples/eg028_brand_creating/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg028 \ No newline at end of file diff --git a/app/eSignature/examples/eg028_brand_creating/views.py b/app/eSignature/examples/eg028_brand_creating/views.py deleted file mode 100644 index d42fb53c..00000000 --- a/app/eSignature/examples/eg028_brand_creating/views.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Example 028: Creating a brand""" - -from os import path -from docusign_esign.client.api_exception import ApiException -from flask import current_app as app -from flask import render_template, session, request, Blueprint -from .controller import Eg028Controller -from ....consts import languages -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg028" # reference and url for this example -eg028 = Blueprint("eg028", __name__) - -@eg028.route("/eg028", methods=["POST"]) -@authenticate(eg=eg) -def brand_creating(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render the response - """ - - # 1. Get required arguments - args = Eg028Controller.get_args() - try: - # 2. Call the worker method to create a new brand - response = Eg028Controller.worker(args) - brand_id = response.brands[0].brand_id - app.logger.info(f"Brand has been created. Brand ID: {brand_id}") - - # 3. Render the response - return render_template( - "example_done.html", - title="Brand creating", - h1="Brand creating", - message=f"""The brand has been created and sent!
Brand ID: {brand_id}.""" - ) - - except ApiException as err: - return process_error(err) - -@eg028.route("/eg028", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - return render_template( - "eg028_brand_creating.html", - title="Brand creating", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - languages=languages - ) \ No newline at end of file diff --git a/app/eSignature/examples/eg029_brands_apply_to_envelope/controller.py b/app/eSignature/examples/eg029_brands_apply_to_envelope.py similarity index 85% rename from app/eSignature/examples/eg029_brands_apply_to_envelope/controller.py rename to app/eSignature/examples/eg029_brands_apply_to_envelope.py index 59e42195..bbd3169d 100644 --- a/app/eSignature/examples/eg029_brands_apply_to_envelope/controller.py +++ b/app/eSignature/examples/eg029_brands_apply_to_envelope.py @@ -3,12 +3,12 @@ from docusign_esign import AccountsApi, EnvelopesApi, EnvelopeDefinition, Document, Signer, SignHere, Tabs, Recipients from docusign_esign.client.api_exception import ApiException from flask import request, session -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error -class Eg029Controller: +class Eg029BrandsApplyToEnvelopeController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -39,18 +39,22 @@ def worker(cls, args): 3. Apply the brand to the envelope using SDK """ - # Step 2. Construct your API headers + # Construct your API headers + #ds-snippet-start:eSign29Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign29Step2 - # Step 3. Construct your request body + # Construct your request body + #ds-snippet-start:eSign29Step4 envelope_api = EnvelopesApi(api_client) envelope_definition = cls.make_envelope(args["envelope_args"]) - # Step 4. Call the eSignature REST API + # Call the eSignature REST API response = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) - + #ds-snippet-end:eSign29Step4 return response + #ds-snippet-start:eSign29Step3 @classmethod def make_envelope(cls, args): """ @@ -94,6 +98,7 @@ def make_envelope(cls, args): ) return envelope_definition + #ds-snippet-end:eSign29Step3 @staticmethod def get_brands(args): diff --git a/app/eSignature/examples/eg029_brands_apply_to_envelope/__init__.py b/app/eSignature/examples/eg029_brands_apply_to_envelope/__init__.py deleted file mode 100644 index 0cc93356..00000000 --- a/app/eSignature/examples/eg029_brands_apply_to_envelope/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg029 \ No newline at end of file diff --git a/app/eSignature/examples/eg030_brands_apply_to_template/controller.py b/app/eSignature/examples/eg030_brands_apply_to_template.py similarity index 83% rename from app/eSignature/examples/eg030_brands_apply_to_template/controller.py rename to app/eSignature/examples/eg030_brands_apply_to_template.py index b4054ee6..e6279b38 100644 --- a/app/eSignature/examples/eg030_brands_apply_to_template/controller.py +++ b/app/eSignature/examples/eg030_brands_apply_to_template.py @@ -2,12 +2,12 @@ from docusign_esign.client.api_exception import ApiException from flask import session, request -from ....consts import pattern -from ....docusign import create_api_client -from ....error_handlers import process_error +from ...consts import pattern +from ...docusign import create_api_client +from ...error_handlers import process_error -class Eg030Controller: +class Eg030BrandsApplyToTemplateController: @staticmethod def get_args(): """Get required session and request arguments""" @@ -18,7 +18,7 @@ def get_args(): cc_email = request.form.get("cc_email") cc_name = request.form.get("cc_name") brand_id = request.form.get("brand") - template_id = request.form.get("envelope_template") + template_id = session["template_id"] if cc_email and cc_name: cc_email = pattern.sub("", cc_email) @@ -48,19 +48,25 @@ def worker(cls, args): 3. Apply the brand to the envelope using the SDK """ - # Step 2. Construct your API headers + # Construct your API headers + #ds-snippet-start:eSign30Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign30Step2 - # Step 3. Construct your request body + # Construct your request body envelope_api = EnvelopesApi(api_client) + #ds-snippet-start:eSign30Step3 envelope_definition = cls.make_envelope(args["envelope_args"]) + #ds-snippet-end:eSign30Step3 - # Step 4. Call the eSignature REST API + # Call the eSignature REST API + #ds-snippet-start:eSign30Step4 response = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) - + #ds-snippet-end:eSign30Step4 return response @classmethod + #ds-snippet-start:eSign30Step3 def make_envelope(cls, args): """ Creates the envelope definition object @@ -92,10 +98,11 @@ def make_envelope(cls, args): envelope_definition.template_roles = [signer] return envelope_definition + #ds-snippet-end:eSign30Step3 @staticmethod def get_data(args): - """Retrieve brands and envelope templates""" + """Retrieve brands""" api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) try: @@ -103,11 +110,7 @@ def get_data(args): account_api = AccountsApi(api_client) brands = account_api.list_brands(account_id=args["account_id"]).brands - """Retrieve all templates using the Templates::List""" - template_api = TemplatesApi(api_client) - envelope_templates = template_api.list_templates(account_id=args["account_id"]).envelope_templates - - return brands, envelope_templates + return brands except ApiException as err: return process_error(err) diff --git a/app/eSignature/examples/eg030_brands_apply_to_template/__init__.py b/app/eSignature/examples/eg030_brands_apply_to_template/__init__.py deleted file mode 100644 index 9dd04620..00000000 --- a/app/eSignature/examples/eg030_brands_apply_to_template/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg030 \ No newline at end of file diff --git a/app/eSignature/examples/eg030_brands_apply_to_template/views.py b/app/eSignature/examples/eg030_brands_apply_to_template/views.py deleted file mode 100644 index 217fc4c3..00000000 --- a/app/eSignature/examples/eg030_brands_apply_to_template/views.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Example 030: Applying a brand and template to an envelope""" - -from os import path -from docusign_esign.client.api_exception import ApiException -from flask import current_app as app -from flask import render_template, session, Blueprint -from .controller import Eg030Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg030" # Reference and URL for this example -eg030 = Blueprint("eg030", __name__) - - -@eg030.route("/eg030", methods=["POST"]) -@authenticate(eg=eg) -def brands_apply_to_template(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render a response - """ - - # 1. Get required arguments - args = Eg030Controller.get_args() - try: - # 2: Call the worker method to apply the brand to the template - response = Eg030Controller.worker(args) - envelope_id = response.envelope_id - app.logger.info(f"The brand and template have been applied to the envelope. Envelope ID: {envelope_id}") - - # 3: Render the response - return render_template( - "example_done.html", - title="Applying a brand and template to an envelope", - h1="Applying a brand and template to an envelope", - message=f"The brand and template have been applied to the envelope!
Envelope ID: {envelope_id}." - ) - - except ApiException as err: - return process_error(err) - - -@eg030.route("/eg030", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - args = { - "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} - "base_path": session["ds_base_path"], - "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} - } - brands, templates = Eg030Controller.get_data(args) - return render_template( - "eg030_brands_apply_to_template.html", - title="Applying a brand and template to an envelope", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - brands=brands, - templates=templates, - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"], - ) \ No newline at end of file diff --git a/app/eSignature/examples/eg031_bulk_send/controller.py b/app/eSignature/examples/eg031_bulk_send.py similarity index 72% rename from app/eSignature/examples/eg031_bulk_send/controller.py rename to app/eSignature/examples/eg031_bulk_send.py index 52734f64..53da45d2 100644 --- a/app/eSignature/examples/eg031_bulk_send/controller.py +++ b/app/eSignature/examples/eg031_bulk_send.py @@ -2,15 +2,17 @@ from os import path from docusign_esign import EnvelopesApi, Document, Signer, EnvelopeDefinition, Recipients, \ - BulkEnvelopesApi, TextCustomField, CustomFields -from docusign_esign.models import BulkSendingCopy, BulkSendingList, BulkSendingCopyRecipient, BulkSendRequest, BulkSendBatchStatus + BulkEnvelopesApi, TextCustomField, CustomFields, Tabs, SignHere +from docusign_esign.models import BulkSendingCopy, BulkSendingList, BulkSendingCopyRecipient, BulkSendingCopyTab, \ + BulkSendRequest, BulkSendBatchStatus from flask import request, session -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG -class Eg031Controller: + +class Eg031BulkSendController: @staticmethod def get_args(): # More data validation would be a good idea here @@ -28,12 +30,14 @@ def get_args(): "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} "base_path": session["ds_base_path"], "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} - "signers": [{ - "signer_name": signer_name_1, - "signer_email": signer_email_1, - "cc_email": cc_email_1, - "cc_name": cc_name_1 - }, + "doc_pdf": path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), + "signers": [ + { + "signer_name": signer_name_1, + "signer_email": signer_email_1, + "cc_email": cc_email_1, + "cc_name": cc_name_1 + }, { "signer_name": signer_name_2, "signer_email": signer_email_2, @@ -41,7 +45,6 @@ def get_args(): "cc_name": cc_name_2 } ] - } return args @@ -57,10 +60,13 @@ def worker(cls, args): 7. Confirm sending success """ - # Step 2. Construct your API headers + # Construct your API headers + #ds-snippet-start:eSign31Step2 api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign31Step2 - # Step 3. Submit a bulk list + # Submit a bulk list + #ds-snippet-start:eSign31Step3 bulk_envelopes_api = BulkEnvelopesApi(api_client) bulk_sending_list = cls.create_bulk_sending_list(args["signers"]) bulk_list = bulk_envelopes_api.create_bulk_send_list( @@ -68,14 +74,18 @@ def worker(cls, args): bulk_sending_list=bulk_sending_list ) bulk_list_id = bulk_list.list_id + #ds-snippet-end:eSign31Step3 - # Step 4. Create an envelope + # Create an envelope + #ds-snippet-start:eSign31Step4 envelope_api = EnvelopesApi(api_client) - envelope_definition = cls.make_draft_envelope() + envelope_definition = cls.make_draft_envelope(args["doc_pdf"]) envelope = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) envelope_id = envelope.envelope_id + #ds-snippet-end:eSign31Step4 - # Step 5. Attach your bulk list id to the envelope + # Attach your bulk list id to the envelope + #ds-snippet-start:eSign31Step5 text_custom_fields = TextCustomField(name="mailingListId", required="false", show="false", value=bulk_list_id) custom_fields = CustomFields(list_custom_fields=[], text_custom_fields=[text_custom_fields]) envelope_api.create_custom_fields( @@ -83,39 +93,10 @@ def worker(cls, args): envelope_id=envelope_id, custom_fields=custom_fields ) + #ds-snippet-end:eSign31Step5 - # Step 6. Add placeholder recipients - cc = Signer( - name="Multi Bulk Recipient::cc", - email="multiBulkRecipients-cc@docusign.com", - role_name="cc", - note="", - routing_order="1", - status="created", - delivery_method="email", - recipient_id="12", - recipient_type="signer" - ) - - signer = Signer( - name="Multi Bulk Recipient::signer", - email="multiBulkRecipients-signer@docusign.com", - role_name="signer", - note="", - routing_order="1", - status="created", - delivery_method="email", - recipient_id="13", - recipient_type="signer" - ) - - envelope_api.create_recipient( - account_id=args["account_id"], - envelope_id=envelope_id, - recipients=Recipients(signers=[signer, cc]) - ) - - # Step 7. Initiate bulk send + # Initiate bulk send + #ds-snippet-start:eSign31Step6 bulk_send_request = BulkSendRequest(envelope_or_template_id=envelope_id) batch = bulk_envelopes_api.create_bulk_send_request( account_id=args["account_id"], @@ -123,9 +104,14 @@ def worker(cls, args): bulk_send_request=bulk_send_request ) batch_id = batch.batch_id + #ds-snippet-end:eSign31Step6 - # Step 8. Confirm successful batch send - response = bulk_envelopes_api.get_bulk_send_batch_status(account_id=args["account_id"], bulk_send_batch_id=batch_id) + # Confirm successful batch send + #ds-snippet-start:eSign31Step7 + response = bulk_envelopes_api.get_bulk_send_batch_status(account_id=args["account_id"], + bulk_send_batch_id=batch_id) + #ds-snippet-end:eSign31Step7 + print(response) return response @@ -167,15 +153,15 @@ def create_bulk_sending_list(cls, args): ) return bulk_sending_list - + @classmethod - def make_draft_envelope(cls): + def make_draft_envelope(cls, doc_pdf): """ Creates the envelope """ # Open the example file - with open(path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), "rb") as file: + with open(doc_pdf, "rb") as file: content_bytes = file.read() base64_file_content = base64.b64encode(content_bytes).decode("ascii") @@ -186,6 +172,43 @@ def make_draft_envelope(cls): document_id=2 ) + # Add placeholder tabs + + recipient_sign_here = SignHere( + anchor_string="/sn1/", + anchor_units="pixels", + anchor_y_offset="10", + anchor_x_offset="20", + tab_label="RecipentTab" + ) + + # Add placeholder recipients + cc = Signer( + name="Multi Bulk Recipient::cc", + email="multiBulkRecipients-cc@docusign.com", + role_name="cc", + note="", + routing_order="2", + status="created", + delivery_method="email", + recipient_id="1", + recipient_type="signer" + ) + + signer = Signer( + name="Multi Bulk Recipient::signer", + email="multiBulkRecipients-signer@docusign.com", + role_name="signer", + note="", + routing_order="1", + status="created", + delivery_method="email", + recipient_id="2", + recipient_type="signer" + ) + + signer.tabs = Tabs(sign_here_tabs=[recipient_sign_here]) + envelope_definition = EnvelopeDefinition( email_subject="Please Sign", documents=[document], @@ -194,4 +217,6 @@ def make_draft_envelope(cls): recipients={}, ) - return envelope_definition \ No newline at end of file + envelope_definition.recipients = Recipients(signers=[signer], carbon_copies=[cc]) + + return envelope_definition diff --git a/app/eSignature/examples/eg031_bulk_send/__init__.py b/app/eSignature/examples/eg031_bulk_send/__init__.py deleted file mode 100644 index 8fb2250a..00000000 --- a/app/eSignature/examples/eg031_bulk_send/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg031 \ No newline at end of file diff --git a/app/eSignature/examples/eg031_bulk_send/views.py b/app/eSignature/examples/eg031_bulk_send/views.py deleted file mode 100644 index f575f705..00000000 --- a/app/eSignature/examples/eg031_bulk_send/views.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Example 031: Send an envelope to multiple recipients""" - -from os import path -from docusign_esign.client.api_exception import ApiException -from flask import current_app as app -from flask import render_template, Blueprint -from .controller import Eg031Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg031" # reference and url for this example -eg031 = Blueprint("eg031", __name__) - -@eg031.route("/eg031", methods=["POST"]) -@authenticate(eg=eg) -def bulk_send(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render response - """ - - # 1. Get required arguments - args = Eg031Controller.get_args() - try: - # 2. Call the worker method for bulk sending - response = Eg031Controller.worker(args) - batch_id = response.batch_id - app.logger.info(f"The envelope has been sent to multiple recipients.") - - # 3. Render the response - return render_template("example_done.html", - title="Bulk sending envelopes to multiple recipients", - h1="Bulk sending envelopes to multiple recipients", - message=f"""The envelope has been sent to recipients!
- Batch id: {batch_id}.""" - ) - - except ApiException as err: - return process_error(err) - - -@eg031.route("/eg031", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - return render_template( - "eg031_bulk_send.html", - title="Bulk sending envelopes to multiple recipients", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"] - ) \ No newline at end of file diff --git a/app/eSignature/examples/eg032_pause_signature_workflow/controller.py b/app/eSignature/examples/eg032_pause_signature_workflow.py similarity index 91% rename from app/eSignature/examples/eg032_pause_signature_workflow/controller.py rename to app/eSignature/examples/eg032_pause_signature_workflow.py index 33ac65e6..3dcf53ac 100644 --- a/app/eSignature/examples/eg032_pause_signature_workflow/controller.py +++ b/app/eSignature/examples/eg032_pause_signature_workflow.py @@ -5,12 +5,12 @@ from docusign_esign.models import Workflow, WorkflowStep from flask import session, request -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG -class Eg032Controller: +class Eg032PauseSignatureWorkflowController: @staticmethod def get_args(): """Get request and session arguments""" @@ -43,18 +43,24 @@ def worker(cls, args): """ envelope_args = args["envelope_args"] - # 1. Create the envelope request object + # Create the envelope request object envelope_definition = cls.make_envelope(envelope_args) + + #ds-snippet-start:eSign32Step2 api_client = create_api_client( base_path=args["base_path"], access_token=args["access_token"] ) - # 2. Call Envelopes::create API method + #ds-snippet-end:eSign32Step2 + + # Call Envelopes::create API method # Exceptions will be caught by the calling function + #ds-snippet-start:eSign32Step4 envelopes_api = EnvelopesApi(api_client) results = envelopes_api.create_envelope( account_id=args["account_id"], envelope_definition=envelope_definition ) + #ds-snippet-end:eSign32Step4 return {"paused_envelope_id": results.envelope_id} @@ -74,6 +80,7 @@ def make_envelope(cls, args): # After resuming (unpause) the signature workflow will send to the second recipient # Create the envelope definition + #ds-snippet-start:eSign32Step3 env = EnvelopeDefinition(email_subject="EnvelopeWorkflowTest") # Read file from a local directory @@ -150,3 +157,4 @@ def make_envelope(cls, args): # To request that the envelope be created as a draft, set to "created" env.status = args["status"] return env + #ds-snippet-end:eSign32Step3 diff --git a/app/eSignature/examples/eg032_pause_signature_workflow/__init__.py b/app/eSignature/examples/eg032_pause_signature_workflow/__init__.py deleted file mode 100644 index 22178fcc..00000000 --- a/app/eSignature/examples/eg032_pause_signature_workflow/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg032 diff --git a/app/eSignature/examples/eg032_pause_signature_workflow/views.py b/app/eSignature/examples/eg032_pause_signature_workflow/views.py deleted file mode 100644 index 4ab7439e..00000000 --- a/app/eSignature/examples/eg032_pause_signature_workflow/views.py +++ /dev/null @@ -1,63 +0,0 @@ -""" Example 032: Creating an envelope where the workflow is paused before the -envelope is sent to a second recipient """ - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, session, Blueprint - -from .controller import Eg032Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg032" # Reference (and URL) for this example -eg032 = Blueprint("eg032", __name__) - - -@eg032.route("/eg032", methods=["POST"]) -@authenticate(eg=eg) -def pause_signature_workflow(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render success response with envelopeId - """ - - # 1. Get required arguments - args = Eg032Controller.get_args() - try: - # 1. Call the worker method - results = Eg032Controller.worker(args) - except ApiException as err: - return process_error(err) - - session["paused_envelope_id"] = results["paused_envelope_id"] # Save for use by other examples which need an envelopeId - - # 2. Render success response with envelopeId - return render_template( - "example_done.html", - title="Envelope sent", - h1="Envelope sent", - message=f"The envelope has been created and sent!" - f"
Envelope ID {results['paused_envelope_id']}.
" - f"

To resume a workflow after the first recipient signs " - f"the envelope use example 33.
" - ) - - -@eg032.route("/eg032", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - return render_template( - "eg032_pause_signature_workflow.html", - title="Pausing a signature workflow", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer1_name=DS_CONFIG["signer_name"], - signer1_email=DS_CONFIG["signer_email"] - ) diff --git a/app/eSignature/examples/eg033_unpause_signature_workflow/controller.py b/app/eSignature/examples/eg033_unpause_signature_workflow.py similarity index 77% rename from app/eSignature/examples/eg033_unpause_signature_workflow/controller.py rename to app/eSignature/examples/eg033_unpause_signature_workflow.py index b726ca09..7e5b493e 100644 --- a/app/eSignature/examples/eg033_unpause_signature_workflow/controller.py +++ b/app/eSignature/examples/eg033_unpause_signature_workflow.py @@ -2,10 +2,10 @@ from docusign_esign.models import Workflow from flask import session -from ....docusign import create_api_client +from ...docusign import create_api_client -class Eg033Controller: +class Eg033UnpauseSignatureWorkflowController: @staticmethod def get_args(): """ Get session arguments """ @@ -23,15 +23,20 @@ def worker(cls, args): """ # Create the envelope definition + #ds-snippet-start:eSign33Step3 env = EnvelopeDefinition(workflow=Workflow(workflow_status="in_progress")) + #ds-snippet-end:eSign33Step3 # Exceptions will be caught by the calling function + #ds-snippet-start:eSign33Step2 api_client = create_api_client( base_path=args["base_path"], access_token=args["access_token"] ) + #ds-snippet-end:eSign33Step2 - # 2. Call Envelopes::update API method + # Call Envelopes::update API method # Exceptions will be caught by the calling function + #ds-snippet-start:eSign33Step4 envelopes_api = EnvelopesApi(api_client) results = envelopes_api.update( account_id=args["account_id"], @@ -39,5 +44,5 @@ def worker(cls, args): envelope=env, resend_envelope=True ) - + #ds-snippet-end:eSign33Step4 return {"envelope_id": results.envelope_id} diff --git a/app/eSignature/examples/eg033_unpause_signature_workflow/__init__.py b/app/eSignature/examples/eg033_unpause_signature_workflow/__init__.py deleted file mode 100644 index fd6c58b4..00000000 --- a/app/eSignature/examples/eg033_unpause_signature_workflow/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg033 diff --git a/app/eSignature/examples/eg033_unpause_signature_workflow/views.py b/app/eSignature/examples/eg033_unpause_signature_workflow/views.py deleted file mode 100644 index 3c96ab62..00000000 --- a/app/eSignature/examples/eg033_unpause_signature_workflow/views.py +++ /dev/null @@ -1,59 +0,0 @@ -""" Example 033: Resuming an envelope workflow that has been paused """ - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, session, Blueprint - -from .controller import Eg033Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg033" # Reference (and URL) for this example -eg033 = Blueprint("eg033", __name__) - - -@eg033.route("/eg033", methods=["POST"]) -@authenticate(eg=eg) -def unpause_signature_workflow(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render success response with envelopeId - """ - - # 1. Get required arguments - args = Eg033Controller.get_args() - try: - # 1. Call the worker method - results = Eg033Controller.worker(args) - except ApiException as err: - return process_error(err) - - # 2. Render success response with envelopeId - return render_template( - "example_done.html", - envelope_ok=True, - title="Envelope unpaused", - h1="Envelope unpaused", - message=f"The envelope workflow has been resumed and the envelope " - f"has been sent to a second recipient!
" - f"Envelope ID {results['envelope_id']}." - ) - - -@eg033.route("/eg033", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - return render_template( - "eg033_unpause_signature_workflow.html", - title="Unpausing a signature workflow", - envelope_ok="paused_envelope_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - ) diff --git a/app/eSignature/examples/eg034_use_conditional_recipients/controller.py b/app/eSignature/examples/eg034_use_conditional_recipients.py similarity index 93% rename from app/eSignature/examples/eg034_use_conditional_recipients/controller.py rename to app/eSignature/examples/eg034_use_conditional_recipients.py index 4adf1bad..6da450ab 100644 --- a/app/eSignature/examples/eg034_use_conditional_recipients/controller.py +++ b/app/eSignature/examples/eg034_use_conditional_recipients.py @@ -12,12 +12,12 @@ ) from flask import session, request -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG -class Eg034Controller: +class Eg034UseConditionalRecipientsController: @staticmethod def get_args(): """Get request and session arguments""" @@ -56,16 +56,20 @@ def worker(cls, args): envelope_args = args["envelope_args"] # 1. Create the envelope request object envelope_definition = cls.make_envelope(envelope_args) + #ds-snippet-start:eSign34Step2 api_client = create_api_client( base_path=args["base_path"], access_token=args["access_token"] ) + #ds-snippet-end:eSign34Step2 # 2. Call Envelopes::create API method # Exceptions will be caught by the calling function + #ds-snippet-start:eSign34Step4 envelopes_api = EnvelopesApi(api_client) results = envelopes_api.create_envelope( account_id=args["account_id"], envelope_definition=envelope_definition ) + #ds-snippet-end:eSign34Step4 return {"envelope_id": results.envelope_id} @@ -84,6 +88,7 @@ def make_envelope(cls, args): # If signer1 doesn't check the checkbox the envelope will be sent to the signer_2a # If signer1 check the checkbox the envelope will be sent to the signer_2b + #ds-snippet-start:eSign34Step3 # Create the envelope definition env = EnvelopeDefinition(email_subject="ApproveIfChecked") @@ -192,7 +197,8 @@ def make_envelope(cls, args): tab_id="ApprovalTab", operator="equals", value="false", - tab_label="ApproveWhenChecked" + tab_label="ApproveWhenChecked", + tab_type ="checkbox" ) filter2 = ConditionalRecipientRuleFilter( scope="tabs", @@ -200,7 +206,8 @@ def make_envelope(cls, args): tab_id="ApprovalTab", operator="equals", value="true", - tab_label="ApproveWhenChecked" + tab_label="ApproveWhenChecked", + tab_type ="checkbox" ) # Create conditionalRecipientRuleCondition models @@ -245,3 +252,4 @@ def make_envelope(cls, args): # To request that the envelope be created as a draft, set to "created" env.status = args["status"] return env + #ds-snippet-end:eSign34Step3 diff --git a/app/eSignature/examples/eg034_use_conditional_recipients/__init__.py b/app/eSignature/examples/eg034_use_conditional_recipients/__init__.py deleted file mode 100644 index e76a708e..00000000 --- a/app/eSignature/examples/eg034_use_conditional_recipients/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg034 diff --git a/app/eSignature/examples/eg034_use_conditional_recipients/views.py b/app/eSignature/examples/eg034_use_conditional_recipients/views.py deleted file mode 100644 index fd0e8722..00000000 --- a/app/eSignature/examples/eg034_use_conditional_recipients/views.py +++ /dev/null @@ -1,60 +0,0 @@ -""" Example 034: Creating an envelope where the workflow is routed -to different recipients based on the value of a transaction """ - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, session, Blueprint - -from .controller import Eg034Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg034" # Reference (and URL) for this example -eg034 = Blueprint("eg034", __name__) - - -@eg034.route("/eg034", methods=["POST"]) -@authenticate(eg=eg) -def use_conditional_recipients(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render success response with envelopeId - """ - - # 1. Get required arguments - args = Eg034Controller.get_args() - try: - # 1. Call the worker method - results = Eg034Controller.worker(args) - except ApiException as err: - return process_error(err) - - # 2. Render success response with envelopeId - return render_template( - "example_done.html", - envelope_ok=True, - title="Use conditional recipients", - h1="Use conditional recipients", - message=f"Envelope ID {results['envelope_id']} with the conditional" - f" routing criteria has been created and sent to the first recipient!" - ) - - -@eg034.route("/eg034", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """Responds with the form for the example""" - - return render_template( - "eg034_use_conditional_recipients.html", - title="Using conditional recipients", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer1_name=DS_CONFIG["signer_name"], - signer1_email=DS_CONFIG["signer_email"] - ) diff --git a/app/eSignature/examples/eg035_scheduled_sending.py b/app/eSignature/examples/eg035_scheduled_sending.py new file mode 100644 index 00000000..de9af175 --- /dev/null +++ b/app/eSignature/examples/eg035_scheduled_sending.py @@ -0,0 +1,125 @@ +import base64 +from os import path + +from docusign_esign import ( + EnvelopesApi, + Envelope, + EnvelopeDefinition, + Document, + Signer, + SignHere, + Tabs, + Recipients, + Workflow, + ScheduledSending, + EnvelopeDelayRule +) + +from ...consts import demo_docs_path, pattern, signer_client_id +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG + + +class Eg035ScheduledSendingController: + + @classmethod + def worker(cls, args): + envelope_args = args["envelope_args"] + print("RESUMEDATE") + print(envelope_args["resume_date"]) + envelope_definition = cls.make_envelope(envelope_args, DS_CONFIG["doc_docx"], DS_CONFIG["doc_pdf"]) + + # Step 3 start + #ds-snippet-start:eSign35Step3 + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + envelopes_api = EnvelopesApi(api_client) + results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + #ds-snippet-end:eSign35Step3 + # Step 3 end + + envelope_id = results.envelope_id + + print("ENVELOPE") + print(envelope_id) + + return {"envelope_id": envelope_id} + + @classmethod + def make_envelope(cls, args, doc_docx_path, doc_pdf_path): + """ + Creates envelope + Document 1: A PDF document. + The recipients" field tags are placed using anchor strings. + """ + + # Step 2 start + #ds-snippet-start:eSign35Step2 + # document 1 (PDF) has sign here anchor tag /sn1/ + # + # The envelope has one recipient. + # recipient 1 - signer + + # create the envelope definition + env = EnvelopeDefinition( + email_subject="Please sign this document" + ) + + with open(path.join(demo_docs_path, doc_pdf_path), "rb") as file: + doc1_pdf_bytes = file.read() + doc1_b64 = base64.b64encode(doc1_pdf_bytes).decode("ascii") + + # Create the document models + document1 = Document( # create the DocuSign document object + document_base64=doc1_b64, + name="Lorem Ipsum", # can be different from actual file name + file_extension="pdf", # many different document types are accepted + document_id="1" # a label used to reference the doc + ) + # The order in the docs array determines the order in the envelope + env.documents = [document1] + + # Create the signer recipient model + signer1 = Signer( + email=args["signer_email"], + name=args["signer_name"], + recipient_id="1", + routing_order="1" + ) + # routingOrder (lower means earlier) determines the order of deliveries + # to the recipients. Parallel routing order is supported by using the + # same integer as the order for two or more recipients. + + # Create signHere fields (also known as tabs) on the documents, + # We're using anchor (autoPlace) positioning + + sign_here1 = SignHere( + anchor_string="/sn1/", + anchor_units="pixels", + anchor_y_offset="10", + anchor_x_offset="20" + ) + + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object wants arrays of the different field/tab types + signer1.tabs = Tabs(sign_here_tabs=[sign_here1]) + + # Add the recipients to the envelope object + recipients = Recipients(signers=[signer1]) + env.recipients = recipients + + workflow = Workflow() + scheduled_sending_api_model = ScheduledSending() + workflow.scheduled_sending = scheduled_sending_api_model + + envelope_delay_rule = EnvelopeDelayRule() + envelope_delay_rule.resume_date = args["resume_date"] + "T00:00:00.000Z" + workflow.scheduled_sending.rules = [envelope_delay_rule] + env.workflow = workflow + + # Request that the envelope be sent by setting |status| to "sent". + # To request that the envelope be created as a draft, set to "created" + env.status = args["status"] + #ds-snippet-end:eSign35Step2 + # Step 2 end + + return env diff --git a/app/eSignature/examples/eg035_sms_delivery/__init__.py b/app/eSignature/examples/eg035_sms_delivery/__init__.py deleted file mode 100644 index 41c2d8d5..00000000 --- a/app/eSignature/examples/eg035_sms_delivery/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg035 diff --git a/app/eSignature/examples/eg035_sms_delivery/views.py b/app/eSignature/examples/eg035_sms_delivery/views.py deleted file mode 100644 index 7f81adbc..00000000 --- a/app/eSignature/examples/eg035_sms_delivery/views.py +++ /dev/null @@ -1,59 +0,0 @@ -""" Example 035: SMS Delivery """ - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, session, Blueprint - -from .controller import Eg035Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error - -eg = "eg035" # reference (and url) for this example -eg035 = Blueprint("eg035", __name__) - - -@eg035.route("/eg035", methods=["POST"]) -@authenticate(eg=eg) -def sign_by_email(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render success response with envelopeId - """ - - # 1. Get required arguments - args = Eg035Controller.get_args() - try: - # 1. Call the worker method - results = Eg035Controller.worker(args) - except ApiException as err: - return process_error(err) - - session["envelope_id"] = results["envelope_id"] # Save for use by other examples which need an envelopeId - - # 2. Render success response with envelopeId - return render_template( - "example_done.html", - title="Envelope sent", - h1="Envelope sent", - message=f"The envelope has been created and sent!
Envelope ID {results['envelope_id']}." - ) - - -@eg035.route("/eg035", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - - return render_template( - "eg035_sms_delivery.html", - title="SMS Delivery", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"] - ) diff --git a/app/eSignature/examples/eg036_delayed_routing.py b/app/eSignature/examples/eg036_delayed_routing.py new file mode 100644 index 00000000..7a8fa703 --- /dev/null +++ b/app/eSignature/examples/eg036_delayed_routing.py @@ -0,0 +1,142 @@ +import base64 +from os import path + +from docusign_esign import ( + EnvelopesApi, + Envelope, + EnvelopeDefinition, + Document, + Signer, + SignHere, + Tabs, + Recipients, + Workflow, + DelayedRouting, + EnvelopeDelayRule, + WorkflowStep +) + +from ...consts import demo_docs_path, pattern, signer_client_id +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG + +class Eg036DelayedRoutingController: + + @classmethod + def worker(cls, args): + + envelope_args = args["envelope_args"] + envelope_definition = cls.make_envelope(envelope_args, DS_CONFIG["doc_docx"], DS_CONFIG["doc_pdf"]) + + #ds-snippet-start:eSign36Step3 + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + envelopes_api = EnvelopesApi(api_client) + results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + #ds-snippet-end:eSign36Step3 + + envelope_id = results.envelope_id + + return {"envelope_id": envelope_id} + + @classmethod + def make_envelope(cls, args, doc_docx_path, doc_pdf_path): + """ + Creates envelope + Document 1: A PDF document. + The recipients' field tags are placed using anchor strings. + """ + + # document 1 (PDF) has sign here anchor tag /sn1/ + # + # The envelope has two recipients. + # recipient 1 - signer + # recipient 2 - second signer + # The envelope will be sent first to the signer. + # After it is signed, there will be a delay before it is sent to the second signer. + + # create the envelope definition + #ds-snippet-start:eSign36Step2 + env = EnvelopeDefinition( + email_subject="Please sign this document" + ) + + with open(path.join(demo_docs_path, doc_pdf_path), "rb") as file: + doc1_pdf_bytes = file.read() + doc1_b64 = base64.b64encode(doc1_pdf_bytes).decode("ascii") + + # Create the document models + document1 = Document( # create the DocuSign document object + document_base64=doc1_b64, + name="Lorem Ipsum", # can be different from actual file name + file_extension="pdf", # many different document types are accepted + document_id="1" # a label used to reference the doc + ) + # The order in the docs array determines the order in the envelope + env.documents = [document1] + + # Create the signer recipient model + signer1 = Signer( + email=args["signer_email"], + name=args["signer_name"], + recipient_id="1", + routing_order="1" + ) + # routingOrder (lower means earlier) determines the order of deliveries + # to the recipients. + + # create a second recipient + signer2 = Signer( + email=args["signer_email2"], + name=args["signer_name2"], + recipient_id="2", + routing_order="2" + ) + + # Create signHere fields (also known as tabs) on the documents, + # We"re using anchor (autoPlace) positioning + # + # The DocuSign platform searches throughout your envelope"s + # documents for matching anchor strings. + + sign_here1 = SignHere( + anchor_string="/sn1/", + anchor_units="pixels", + anchor_y_offset="10", + anchor_x_offset="20" + ) + + sign_here2 = SignHere( + x_position = "320", + y_position = "175", + page_number = "1", + document_id = "1" + ) + + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object wants arrays of the different field/tab types + signer1.tabs = Tabs(sign_here_tabs=[sign_here1]) + signer2.tabs = Tabs(sign_here_tabs=[sign_here2]) + + # Add the recipients to the envelope object + recipients = Recipients(signers=[signer1, signer2]) + env.recipients = recipients + + delay = "0." + args["delay"] + ":00:00" + + workflow = Workflow() + + workflow_step = WorkflowStep() + workflow_step.action = "pause_before" + workflow_step.trigger_on_item = "routing_order" + workflow_step.item_id = "2" + delayed_routing = DelayedRouting(rules=[EnvelopeDelayRule(delay=delay)]) + workflow_step.delayed_routing = delayed_routing + workflow.workflow_steps = [workflow_step] + env.workflow = workflow + + # Request that the envelope be sent by setting |status| to "sent". + # To request that the envelope be created as a draft, set to "created" + env.status = args["status"] + #ds-snippet-end:eSign36Step2 + + return env \ No newline at end of file diff --git a/app/eSignature/examples/eg035_sms_delivery/controller.py b/app/eSignature/examples/eg037_sms_delivery.py similarity index 87% rename from app/eSignature/examples/eg035_sms_delivery/controller.py rename to app/eSignature/examples/eg037_sms_delivery.py index a1a94caf..f6a536e6 100644 --- a/app/eSignature/examples/eg035_sms_delivery/controller.py +++ b/app/eSignature/examples/eg037_sms_delivery.py @@ -10,42 +10,39 @@ SignHere, Tabs, Recipients, - RecipientPhoneNumber, - RecipientAdditionalNotification, + RecipientPhoneNumber ) from flask import session, request -from ....consts import demo_docs_path, pattern -from ....docusign import create_api_client -from ....ds_config import DS_CONFIG +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG -class Eg035Controller: +class Eg037SMSDeliveryController: @staticmethod def get_args(): """Get request and session arguments""" # More data validation would be a good idea here # Strip anything other than characters listed - signer_email = pattern.sub("", request.form.get("signer_email")) signer_name = pattern.sub("", request.form.get("signer_name")) - cc_email = pattern.sub("", request.form.get("cc_email")) cc_name = pattern.sub("", request.form.get("cc_name")) cc_phone_number = request.form.get("cc_phone_number") cc_country_code = request.form.get("country_code") phone_number = request.form.get("phone_number") country_code = request.form.get("country_code") + delivery_method = request.form["delivery_method"] envelope_args = { - "signer_email": signer_email, "signer_name": signer_name, "status": "sent", - "cc_email": cc_email, "cc_name": cc_name, "country_code": country_code, "phone_number": phone_number, "cc_country_code" :cc_country_code, - "cc_phone_number": cc_phone_number + "cc_phone_number": cc_phone_number, + "delivery_method": delivery_method } args = { "account_id": session["ds_account_id"], @@ -62,11 +59,12 @@ def worker(cls, args): 2. Send the envelope """ + #ds-snippet-start:eSign37Step3 envelope_args = args["envelope_args"] - # 1. Create the envelope request object + # Create the envelope request object api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) envelope_definition = cls.make_envelope(envelope_args) - # 2. Call Envelopes::create API method + # Call Envelopes::create API method # Exceptions will be caught by the calling function envelopes_api = EnvelopesApi(api_client) results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) @@ -74,7 +72,9 @@ def worker(cls, args): envelope_id = results.envelope_id return {"envelope_id": envelope_id} + #ds-snippet-end + #ds-snippet-start:eSign37Step2 @classmethod def make_envelope(cls, args): """ @@ -90,7 +90,6 @@ def make_envelope(cls, args): The envelope will be sent first to the signer via SMS. After it is signed, a copy is sent to the cc recipient via SMS. """ - # Create the envelope definition env = EnvelopeDefinition( email_subject="Please sign this document set" @@ -127,23 +126,18 @@ def make_envelope(cls, args): # The order in the docs array determines the order in the envelope env.documents = [document1, document2, document3] - phoneNumber = RecipientPhoneNumber( + signerPhoneNumber = RecipientPhoneNumber( country_code=args["country_code"], number=args["phone_number"] ) - sms_notification = RecipientAdditionalNotification( - phone_number=phoneNumber - ) - sms_notification.secondary_delivery_method = "SMS" - # Create the signer recipient model signer1 = Signer( - email=args["signer_email"], name=args["signer_name"], recipient_id="1", routing_order="1", - additional_notifications=[sms_notification] + delivery_method=args["delivery_method"], + phone_number=signerPhoneNumber ) # Create a RecipientPhoneNumber and add it to the additional SMS notification @@ -152,18 +146,13 @@ def make_envelope(cls, args): number=args["cc_phone_number"] ) - cc_sms_notification = RecipientAdditionalNotification( - phone_number=ccPhoneNumber - ) - cc_sms_notification.secondary_delivery_method = "SMS" - # Create a cc recipient to receive a copy of the documents cc1 = CarbonCopy( - email=args["cc_email"], name=args["cc_name"], recipient_id="2", routing_order="2", - additional_notifications=[cc_sms_notification] + delivery_method=args["delivery_method"], + phone_number=ccPhoneNumber ) # routingOrder (lower means earlier) determines the order of deliveries @@ -222,8 +211,8 @@ def create_document1(cls, args): margin-top: 0px;margin-bottom: 3.5em;font-size: 1em; color: darkblue;">Order Processing Division

Ordered by {args["signer_name"]}

-

Email: {args["signer_email"]}

-

Copy to: {args["cc_name"]}, {args["cc_email"]}

+

Phone Number: {args["phone_number"]}

+

Copy to: {args["cc_name"]}

Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. @@ -236,3 +225,4 @@ def create_document1(cls, args): """ +#ds-snippet-end diff --git a/app/eSignature/examples/eg038_responsive_signing.py b/app/eSignature/examples/eg038_responsive_signing.py new file mode 100644 index 00000000..83bb8836 --- /dev/null +++ b/app/eSignature/examples/eg038_responsive_signing.py @@ -0,0 +1,210 @@ +from os import path + +from docusign_esign import ( + EnvelopesApi, + RecipientViewRequest, + Document, + Signer, + CarbonCopy, + EnvelopeDefinition, + Recipients, + DocumentHtmlDefinition, + FormulaTab, + Tabs +) +from flask import session, url_for, request + +from ...consts import authentication_method, demo_docs_path, pattern, signer_client_id +from ...docusign import create_api_client + + +class Eg038ResponsiveSigning: + @staticmethod + def get_args(): + """Get request and session arguments""" + # More data validation would be a good idea here + # Strip anything other than characters listed + # 1. Parse request arguments + signer_email = pattern.sub("", request.form.get("signer_email")) + signer_name = pattern.sub("", request.form.get("signer_name")) + cc_email = pattern.sub("", request.form.get("cc_email")) + cc_name = pattern.sub("", request.form.get("cc_name")) + envelope_args = { + "signer_email": signer_email, + "signer_name": signer_name, + "cc_email": cc_email, + "cc_name": cc_name, + "signer_client_id": signer_client_id, + "ds_return_url": url_for("ds.ds_return", _external=True), + "doc_file": path.join(demo_docs_path, "order_form.html") + } + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_args": envelope_args + } + return args + + @classmethod + #ds-snippet-start:eSign38Step3 + def worker(cls, args): + """ + 1. Create the envelope request object + 2. Send the envelope + 3. Create the Recipient View request object + 4. Obtain the recipient_view_url for the embedded signing + """ + envelope_args = args["envelope_args"] + # Create the envelope request object + envelope_definition = cls.make_envelope(envelope_args) + + # Call Envelopes::create API method + # Exceptions will be caught by the calling function + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + + envelope_api = EnvelopesApi(api_client) + results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + + envelope_id = results.envelope_id + + # Create the Recipient View request object + recipient_view_request = RecipientViewRequest( + authentication_method=authentication_method, + client_user_id=envelope_args["signer_client_id"], + recipient_id="1", + return_url=envelope_args["ds_return_url"], + user_name=envelope_args["signer_name"], + email=envelope_args["signer_email"] + ) + # Obtain the recipient_view_url for the embedded signing + # Exceptions will be caught by the calling function + results = envelope_api.create_recipient_view( + account_id=args["account_id"], + envelope_id=envelope_id, + recipient_view_request=recipient_view_request + ) + + return {"envelope_id": envelope_id, "redirect_url": results.url} + #ds-snippet-end:eSign38Step3 + + @classmethod + #ds-snippet-start:eSign38Step2 + def make_envelope(cls, args): + """ + Creates envelope + args -- parameters for the envelope: + signer_email, signer_name, signer_client_id + returns an envelope definition + """ + + html_definition = DocumentHtmlDefinition( + source=cls.get_html_content(args) + ) + + # Create the document model + document = Document( # create the DocuSign document object + html_definition=html_definition, + name="doc1.html", # can be different from actual file name + document_id=1 # a label used to reference the doc + ) + + price_1 = 5 + formula_tab_1 = FormulaTab( + font="helvetica", + font_size="size11", + font_color="black", + anchor_string="/l1e/", + anchor_y_offset="-8", + anchor_units="pixels", + anchor_x_offset="105", + tab_label="l1e", + formula=f"[l1q] * {price_1}", + round_decimal_places="0", + required="true", + locked="true", + disable_auto_size="false" + ) + + price_2 = 150 + formula_tab_2 = FormulaTab( + font="helvetica", + font_size="size11", + font_color="black", + anchor_string="/l2e/", + anchor_y_offset="-8", + anchor_units="pixels", + anchor_x_offset="105", + tab_label="l2e", + formula=f"[l2q] * {price_2}", + round_decimal_places="0", + required="true", + locked="true", + disable_auto_size="false" + ) + + formula_tab_3 = FormulaTab( + font="helvetica", + font_size="size11", + font_color="black", + anchor_string="/l3t/", + anchor_y_offset="-8", + anchor_units="pixels", + anchor_x_offset="105", + tab_label="l3t", + formula="[l1e] + [l2e]", + round_decimal_places="0", + required="true", + locked="true", + disable_auto_size="false" + ) + + tabs = Tabs( + formula_tabs=[formula_tab_1, formula_tab_2, formula_tab_3] + ) + + # Create the signer recipient model + signer = Signer( + # The signer + email=args["signer_email"], + name=args["signer_name"], + recipient_id="1", + routing_order="1", + # Setting the client_user_id marks the signer as embedded + client_user_id=args["signer_client_id"], + role_name="Signer", + tabs=tabs + ) + + cc = CarbonCopy( + email=args["cc_email"], + name=args["cc_name"], + recipient_id="2", + routing_order="2" + ) + + # Next, create the top level envelope definition and populate it. + envelope_definition = EnvelopeDefinition( + email_subject="Example Signing Document", + documents=[document], + # The Recipients object wants arrays for each recipient type + recipients=Recipients(signers=[signer], carbon_copies=[cc]), + status="sent" # requests that the envelope be created and sent. + ) + + return envelope_definition + + @classmethod + def get_html_content(cls, args): + with open(args["doc_file"], "r") as file: + doc_html = file.read() + + return doc_html.replace("{signer_name}", args["signer_name"]) \ + .replace("{signer_email}", args["signer_email"]) \ + .replace("{cc_name}", args["cc_name"]) \ + .replace("{cc_email}", args["cc_email"]) \ + .replace("/sn1/", "") \ + .replace("/l1q/", "") \ + .replace("/l2q/", "") + +#ds-snippet-end:eSign38Step2 diff --git a/app/eSignature/examples/eg039_in_person_signer.py b/app/eSignature/examples/eg039_in_person_signer.py new file mode 100644 index 00000000..dee7f32c --- /dev/null +++ b/app/eSignature/examples/eg039_in_person_signer.py @@ -0,0 +1,141 @@ +import base64 +from os import path + +from docusign_esign import EnvelopesApi, RecipientViewRequest, Document, Signer, EnvelopeDefinition, SignHere, Tabs, \ + Recipients, InPersonSigner +from flask import session, url_for, request + +from ...consts import authentication_method, demo_docs_path, pattern, signer_client_id +from ...docusign import create_api_client, DSClient +from ...ds_config import DS_CONFIG + + +class Eg039InPersonSigner: + @staticmethod + def get_args(): + """Get request and session arguments""" + # More data validation would be a good idea here + # Strip anything other than characters listed + # Parse request arguments + signer_name = pattern.sub("", request.form.get("signer_name")) + envelope_args = { + "host_email": session["ds_user_email"], + "host_name": session["ds_user_name"], + "signer_name": signer_name, + "ds_return_url": url_for("ds.ds_return", _external=True), + } + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_args": envelope_args + } + return args + + @classmethod + # Step 3 start + def worker(cls, args): + """ + 1. Create the envelope request object + 2. Send the envelope + 3. Create the Recipient View request object + 4. Obtain the recipient_view_url for the embedded signing + """ + #ds-snippet-start:eSign39Step3 + envelope_args = args["envelope_args"] + # Create the envelope request object + envelope_definition = cls.make_envelope(envelope_args) + + # Call Envelopes::create API method + # Exceptions will be caught by the calling function + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + + envelope_api = EnvelopesApi(api_client) + results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + + envelope_id = results.envelope_id + #ds-snippet-end:eSign39Step3 + + # Create the Recipient View request object + #ds-snippet-start:eSign39Step4 + recipient_view_request = RecipientViewRequest( + authentication_method=authentication_method, + recipient_id="1", + return_url=envelope_args["ds_return_url"], + user_name=envelope_args["host_name"], + email=envelope_args["host_email"] + ) + #ds-snippet-end:eSign39Step4 + + # Obtain the recipient_view_url for the embedded signing session + # Exceptions will be caught by the calling function + #ds-snippet-start:eSign39Step5 + results = envelope_api.create_recipient_view( + account_id=args["account_id"], + envelope_id=envelope_id, + recipient_view_request=recipient_view_request + ) + return {"envelope_id": envelope_id, "redirect_url": results.url} + #ds-snippet-end:eSign39Step5 + + @classmethod + #ds-snippet-start:eSign39Step2 + def make_envelope(cls, args): + """ + Creates envelope + args -- parameters for the envelope: + signer_email, signer_name, signer_client_id + returns an envelope definition + """ + + # document 1 (pdf) has tag /sn1/ + # + # The envelope has one recipient. + # recipient 1 - signer + with open(path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), "rb") as file: + content_bytes = file.read() + base64_file_content = base64.b64encode(content_bytes).decode("ascii") + + # Create the document model + document = Document( # create the DocuSign document object + document_base64=base64_file_content, + name="Example document", # can be different from actual file name + file_extension="pdf", # many different document types are accepted + document_id=1 # a label used to reference the doc + ) + + # Create the in person signer recipient model + signer = InPersonSigner( + # The signer + host_name = args["host_name"], + host_email = args["host_email"], + signer_name = args["signer_name"], + recipient_id="1", + routing_order="1", + + ) + + # Create a sign_here tab (field on the document) + sign_here = SignHere( + # DocuSign SignHere field/tab + anchor_string="/sn1/", + anchor_units="pixels", + anchor_y_offset="10", + anchor_x_offset="20" + ) + + # Add the tabs model (including the sign_here tab) to the signer + # The Tabs object wants arrays of the different field/tab types + InPersonSigner.tabs = Tabs(sign_here_tabs=[sign_here]) + + # Next, create the top level envelope definition and populate it. + envelope_definition = EnvelopeDefinition( + email_subject="Please host this in-person signing session", + documents=[document], + # The Recipients object wants arrays for each recipient type + recipients=Recipients(in_person_signers=[signer]), + status="sent" # requests that the envelope be created and sent. + ) + + return envelope_definition + #ds-snippet-end:eSign39Step2 diff --git a/app/eSignature/examples/eg040_document_visibility.py b/app/eSignature/examples/eg040_document_visibility.py new file mode 100644 index 00000000..f940518f --- /dev/null +++ b/app/eSignature/examples/eg040_document_visibility.py @@ -0,0 +1,216 @@ +import base64 +from os import path +from typing import List + +from docusign_esign import ( + EnvelopesApi, + Document, + Signer, + CarbonCopy, + EnvelopeDefinition, + SignHere, + Tabs, + Recipients, +) +from flask import session, request + +from ...consts import demo_docs_path, pattern +from ...docusign import create_api_client + + +class Eg040DocumentVisibility: + @staticmethod + def get_args(): + """Get request and session arguments""" + # More data validation would be a good idea here + # Strip anything other than characters listed + # Parse request arguments + signer1_email = pattern.sub("", request.form.get("signer1_email")) + signer1_name = pattern.sub("", request.form.get("signer1_name")) + signer2_email = pattern.sub("", request.form.get("signer2_email")) + signer2_name = pattern.sub("", request.form.get("signer2_name")) + cc_email = pattern.sub("", request.form.get("cc_email")) + cc_name = pattern.sub("", request.form.get("cc_name")) + envelope_args = { + "signer1_email": signer1_email, + "signer1_name": signer1_name, + "signer2_email": signer2_email, + "signer2_name": signer2_name, + "cc_email": cc_email, + "cc_name": cc_name, + "status": "sent", + } + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_args": envelope_args + } + return args + + @classmethod + def worker(cls, args, doc_docx_path, doc_pdf_path): + """ + 1. Create the envelope request object + 2. Send the envelope + """ + envelope_args = args["envelope_args"] + # Create the envelope request object + envelope_definition = cls.make_envelope(envelope_args, doc_docx_path, doc_pdf_path) + + # Call Envelopes::create API method + # Exceptions will be caught by the calling function + #ds-snippet-start:eSign40Step2 + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign40Step2 + + #ds-snippet-start:eSign40Step4 + envelope_api = EnvelopesApi(api_client) + results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + envelope_id = results.envelope_id + #ds-snippet-end:eSign40Step4 + return {"envelope_id": envelope_id} + + @classmethod + #ds-snippet-start:eSign40Step3 + def make_envelope(cls, args, doc_docx_path, doc_pdf_path): + """ + Creates envelope + args -- parameters for the envelope: + signer_email, signer_name, signer_client_id + returns an envelope definition + """ + env = EnvelopeDefinition( + email_subject = "Please sign this document set", + enforce_signer_visibility = "true", + ) + + doc1_b64 = base64.b64encode(bytes(cls.create_document1(args), "utf-8")).decode("ascii") + # read files 2 and 3 from a local directory + # The reads could raise an exception if the file is not available! + with open(path.join(demo_docs_path, doc_docx_path), "rb") as file: + doc2_docx_bytes = file.read() + doc2_b64 = base64.b64encode(doc2_docx_bytes).decode("ascii") + with open(path.join(demo_docs_path, doc_pdf_path), "rb") as file: + doc3_pdf_bytes = file.read() + doc3_b64 = base64.b64encode(doc3_pdf_bytes).decode("ascii") + + # Create the document models + document1 = Document( # create the DocuSign document object + document_base64=doc1_b64, + name="Order acknowledgement", # can be different from actual file name + file_extension="html", # many different document types are accepted + document_id="1", # a label used to reference the doc + ) + document2 = Document( + document_base64=doc2_b64, + name="Battle Plan", + file_extension="docx", + document_id="2" + ) + document3 = Document( + document_base64=doc3_b64, + name="Lorem Ipsum", + file_extension="pdf", + document_id="3" + ) + + # The order in the docs array determines the order in the envelope + env.documents = [document1, document2, document3] + + # Create the signer recipient model + signer1 = Signer( + # The signer1 + email=args["signer1_email"], + name=args["signer1_name"], + excluded_documents = ["2","3"], # Sets which documents are excluded for this signer + recipient_id="1", + routing_order="1", + role_name="Signer1" + ) + + signer2 = Signer( + # The signer2 + email=args["signer2_email"], + name=args["signer2_name"], + excluded_documents = ["1"], + recipient_id="2", + routing_order="1", + role_name="Signer2" + ) + + cc = CarbonCopy( + email=args["cc_email"], + name=args["cc_name"], + recipient_id="3", + routing_order="2" + ) + + # Create signHere fields (also known as tabs) on the documents. + sign_here1 = SignHere( + document_id="1", + page_number="1", + name="Signer1", + anchor_string="**signature_1**", + anchor_units="pixels", + anchor_y_offset="10", + anchor_x_offset="20" + ) + sign_here2 = SignHere( + document_id="2", + anchor_string="/sn1/", + anchor_units="pixels", + anchor_y_offset="10", + anchor_x_offset="20" + ) + + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object wants arrays of the different field/tab types + signer1.tabs = Tabs( + sign_here_tabs=[sign_here1, ], + ) + + signer2.tabs = Tabs(sign_here_tabs=[sign_here2, ]) + + # Add the recipients to the envelope object + recipients = Recipients(signers=[signer1, signer2], carbon_copies=[cc]) + env.recipients = recipients + + # Request that the envelope be sent by setting |status| to "sent". + # To request that the envelope be created as a draft, set to "created" + env.status = args["status"] + + return env + + @classmethod + def create_document1(cls, args): + """ Creates document 1 -- an html document""" + + return f""" + + + + + + +

World Wide Corp

+

Order Processing Division

+

Ordered by {args["signer1_name"]}

+

Email: {args["signer1_email"]}

+

Copy to: {args["cc_name"]}, {args["cc_email"]}

+

+ Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. + Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. + Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. + Donut jujubes oat cake jelly-o. + Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake. +

+ +

Agreed: **signature_1**/

+ + + """ + #ds-snippet-end:eSign40Step3 diff --git a/app/eSignature/examples/eg041_cfr_embedded_signing.py b/app/eSignature/examples/eg041_cfr_embedded_signing.py new file mode 100644 index 00000000..51bb852e --- /dev/null +++ b/app/eSignature/examples/eg041_cfr_embedded_signing.py @@ -0,0 +1,176 @@ +import base64 +from os import path + +from docusign_esign import AccountsApi, EnvelopesApi, RecipientViewRequest, Document, Signer, EnvelopeDefinition, SignHere, Tabs, \ + Recipients +from docusign_esign.client.api_exception import ApiException +from flask import session, url_for, request + +from ...consts import authentication_method, demo_docs_path, pattern, signer_client_id +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG + + +class Eg041CFREmbeddedSigningController: + @staticmethod + def get_args(): + """Get request and session arguments""" + # More data validation would be a good idea here + # Strip anything other than characters listed + # 1. Parse request arguments + signer_email = pattern.sub("", request.form.get("signer_email")) + signer_name = pattern.sub("", request.form.get("signer_name")) + country_code = pattern.sub("", request.form.get("country_code")) + phone_number = pattern.sub("", request.form.get("phone_number")) + envelope_args = { + "signer_email": signer_email, + "signer_name": signer_name, + "phone_number": phone_number, + "country_code": country_code, + "signer_client_id": signer_client_id, + "ds_return_url": url_for("ds.ds_return", _external=True), + "workflow_id": session['workflow_id'] + } + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_args": envelope_args + } + return args + + @classmethod + def worker(cls, args): + """ + 1. Create the envelope request object + 2. Send the envelope + 3. Create the Recipient View request object + 4. Obtain the recipient_view_url for the embedded signing + """ + envelope_args = args["envelope_args"] + # Create the envelope request object + envelope_definition = cls.make_envelope(envelope_args) + + # Call Envelopes::create API method + # Exceptions will be caught by the calling function + #ds-snippet-start:eSign41Step4 + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + + envelope_api = EnvelopesApi(api_client) + results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + + envelope_id = results.envelope_id + #ds-snippet-end:eSign41Step4 + + # Create the Recipient View request object + #ds-snippet-start:eSign41Step5 + recipient_view_request = RecipientViewRequest( + authentication_method=authentication_method, + client_user_id=envelope_args["signer_client_id"], + recipient_id="1", + return_url=envelope_args["ds_return_url"], + user_name=envelope_args["signer_name"], + email=envelope_args["signer_email"] + ) + #ds-snippet-end:eSign41Step5 + # Obtain the recipient_view_url for the embedded signing + # Exceptions will be caught by the calling function + #ds-snippet-start:eSign41Step6 + results = envelope_api.create_recipient_view( + account_id=args["account_id"], + envelope_id=envelope_id, + recipient_view_request=recipient_view_request + ) + #ds-snippet-end:eSign41Step6 + + return {"envelope_id": envelope_id, "redirect_url": results.url} + + #ds-snippet-start:eSign41Step3 + @classmethod + def make_envelope(cls, args): + """ + Creates envelope + args -- parameters for the envelope: + signer_email, signer_name, signer_client_id + returns an envelope definition + """ + + # document 1 (pdf) has tag /sn1/ + # + # The envelope has one recipient. + # recipient 1 - signer + with open(path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), "rb") as file: + content_bytes = file.read() + base64_file_content = base64.b64encode(content_bytes).decode("ascii") + + # Create the document model + document = Document( # create the DocuSign document object + document_base64=base64_file_content, + name="Example document", # can be different from actual file name + file_extension="pdf", # many different document types are accepted + document_id=1 # a label used to reference the doc + ) + + # Create the signer recipient model + signer = Signer( + # The signer + email=args["signer_email"], + name=args["signer_name"], + recipient_id="1", + routing_order="1", + identity_verification={ "workflowId": session['workflow_id'], "steps": "null", "inputOptions":[{"name":"phone_number_list","valueType":"PhoneNumberList","phoneNumberList":[{"countryCode":args["country_code"],"code":"1","number":args["phone_number"]}]}], "idCheckConfigurationName":""}, + # Setting the client_user_id marks the signer as embedded + client_user_id=args["signer_client_id"] + ) + + # Create a sign_here tab (field on the document) + sign_here = SignHere( + # DocuSign SignHere field/tab + anchor_string="/sn1/", + anchor_units="pixels", + anchor_y_offset="-30", + anchor_x_offset="20" + ) + + # Add the tabs model (including the sign_here tab) to the signer + # The Tabs object wants arrays of the different field/tab types + signer.tabs = Tabs(sign_here_tabs=[sign_here]) + + # Next, create the top level envelope definition and populate it. + envelope_definition = EnvelopeDefinition( + email_subject="Please sign this document sent from the Python SDK", + documents=[document], + # The Recipients object wants arrays for each recipient type + recipients=Recipients(signers=[signer]), + status="sent" # requests that the envelope be created and sent. + ) + + return envelope_definition + #ds-snippet-end:eSign41Step3 + + #ds-snippet-start:eSign41Step2 + @staticmethod + def get_workflow(args): + """Retrieve the workflow id""" + try: + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + + workflow_details = AccountsApi(api_client) + workflow_response = workflow_details.get_account_identity_verification(account_id=args["account_id"]) + + # Check that idv authentication is enabled + # Find the workflow ID corresponding to the name "Phone Authentication" + if workflow_response.identity_verification: + for workflow in workflow_response.identity_verification: + if workflow.default_name == "SMS for access & signatures": + session['workflow_id'] = workflow.workflow_id + + return session['workflow_id'] + + else: + return None + + except ApiException as err: + return process_error(err) + + #ds-snippet-end:eSign41Step2 \ No newline at end of file diff --git a/app/eSignature/examples/eg042_document_generation.py b/app/eSignature/examples/eg042_document_generation.py new file mode 100644 index 00000000..7edd5cf3 --- /dev/null +++ b/app/eSignature/examples/eg042_document_generation.py @@ -0,0 +1,270 @@ +import base64 +from flask import session, request +from os import path +from docusign_esign import EnvelopesApi, TemplatesApi, EnvelopeDefinition, Document, Signer, SignHere, \ + DateSigned, Tabs, Recipients, DocGenFormField, EnvelopeTemplate, TemplateRole, DocGenFormFields, \ + DocGenFormFieldRequest, Envelope, DocGenFormFieldRowValue + +from ...consts import demo_docs_path, pattern +from ...ds_config import DS_CONFIG +from ...docusign import create_api_client + + +class Eg042DocumentGenerationController: + @staticmethod + def get_args(): + """Get request and session arguments""" + envelope_args = { + "candidate_email": pattern.sub("", request.form.get("candidate_email")), + "candidate_name": pattern.sub("", request.form.get("candidate_name")), + "manager_name": pattern.sub("", request.form.get("manager_name")), + "job_title": pattern.sub("", request.form.get("job_title")), + "salary": pattern.sub("", request.form.get("salary")), + "rsus": pattern.sub("", request.form.get("rsus")), + "start_date": pattern.sub("", request.form.get("start_date")), + "doc_file": path.join(demo_docs_path, DS_CONFIG["doc_dynamic_table"]) + } + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_args": envelope_args + } + return args + + @classmethod + def worker(cls, args): + """ + 1. Create the template + 2. Update template document + 3. Update recipient tabs + 4. Create draft envelope + 5. Get the document id + 6. Merge the data fields + 7. Send the envelope + """ + + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + templates_api = TemplatesApi(api_client) + envelopes_api = EnvelopesApi(api_client) + + account_id = args["account_id"] + envelope_args = args["envelope_args"] + + #ds-snippet-start:eSign42Step2 + template_data = cls.make_template() + template = templates_api.create_template(account_id, envelope_template=template_data) + template_id = template.template_id + #ds-snippet-end:eSign42Step2 + + # Update template document + #ds-snippet-start:eSign42Step3 + document_id = '1' + templates_api.update_document( + account_id, document_id, template_id, + envelope_definition=cls.template_document(envelope_args) + ) + #ds-snippet-end:eSign42Step3 + + # Update recipient tabs + #ds-snippet-start:eSign42Step4 + recipient_id = '1' + templates_api.create_tabs( + account_id, recipient_id, template_id, + template_tabs=cls.recipient_tabs() + ) + #ds-snippet-end:eSign42Step4 + + # Create draft envelope + #ds-snippet-start:eSign42Step5 + envelope_definition = cls.make_envelope(template_id, envelope_args) + envelope = envelopes_api.create_envelope(account_id, envelope_definition=envelope_definition) + envelope_id = envelope.envelope_id + #ds-snippet-end:eSign42Step5 + + # Get the document id + #ds-snippet-start:eSign42Step6 + doc_gen_form_fields_response = envelopes_api.get_envelope_doc_gen_form_fields(account_id, envelope_id) + document_id_guid = doc_gen_form_fields_response.doc_gen_form_fields[0].document_id + #ds-snippet-end:eSign42Step6 + + # Merge the data fields + #ds-snippet-start:eSign42Step7 + form_fields = cls.form_fields(envelope_args, document_id_guid) + envelopes_api.update_envelope_doc_gen_form_fields( + account_id, + envelope_id, + doc_gen_form_field_request=form_fields + ) + #ds-snippet-end:eSign42Step7 + + # Send the envelope + #ds-snippet-start:eSign42Step8 + send_envelope_req = Envelope(status="sent") + envelope = envelopes_api.update(account_id, envelope_id, envelope=send_envelope_req) + #ds-snippet-end:eSign42Step8 + return envelope + + #ds-snippet-start:eSign42Step2 + @classmethod + def make_template(cls): + # Create recipient + signer = Signer( + role_name="signer", + recipient_id="1", + routing_order="1", + ) + recipients = Recipients( + signers=[signer] + ) + + # Create the envelope template model + template_request = EnvelopeTemplate( + name="Example document generation template", + description="Example template created via the API", + email_subject="Please sign this document", + shared="false", + recipients=recipients, + status="created" + ) + return template_request + + #ds-snippet-end:eSign42Step2 + + #ds-snippet-start:eSign42Step3 + @classmethod + def template_document(cls, args): + with open(args["doc_file"], "rb") as file: + content_bytes = file.read() + base64_file_content = base64.b64encode(content_bytes).decode("ascii") + + # Create the document model + document = Document( + document_base64=base64_file_content, + name="OfferLetterDemo.docx", + file_extension="docx", + document_id=1, + order=1, + pages=1 + ) + + envelope_definition = EnvelopeDefinition( + documents=[document] + ) + return envelope_definition + #ds-snippet-end:eSign42Step3 + + #ds-snippet-start:eSign42Step4 + @classmethod + def recipient_tabs(cls): + # Create tabs + sign_here = SignHere( + anchor_string="Employee Signature", + anchor_units="pixels", + anchor_x_offset="5", + anchor_y_offset="-22" + ) + date_signed = DateSigned( + anchor_string="Date Signed", + anchor_units="pixels", + anchor_y_offset="-22" + ) + tabs = Tabs( + sign_here_tabs=[sign_here], + date_signed_tabs=[date_signed] + ) + return tabs + #ds-snippet-end:eSign42Step4 + + #ds-snippet-start:eSign42Step5 + @classmethod + def make_envelope(cls, template_id, args): + # Create the signer model + signer = TemplateRole( + email=args["candidate_email"], + name=args["candidate_name"], + role_name="signer" + ) + + # Create the envelope model + envelope_definition = EnvelopeDefinition( + template_roles=[signer], + status="created", + template_id=template_id + ) + return envelope_definition + #ds-snippet-end:eSign42Step5 + + #ds-snippet-start:eSign42Step7 + @classmethod + def form_fields(cls, args, document_id_guid): + bonus_value = "20%" + doc_gen_form_field_request = DocGenFormFieldRequest( + doc_gen_form_fields=[ + DocGenFormFields( + document_id=document_id_guid, + doc_gen_form_field_list=[ + DocGenFormField( + name="Candidate_Name", + value=args["candidate_name"] + ), + DocGenFormField( + name="Manager_Name", + value=args["manager_name"] + ), + DocGenFormField( + name="Job_Title", + value=args["job_title"] + ), + DocGenFormField( + name="Start_Date", + value=args["start_date"] + ), + DocGenFormField( + name="Compensation_Package", + type="TableRow", + row_values=[ + DocGenFormFieldRowValue( + doc_gen_form_field_list=[ + DocGenFormField( + name="Compensation_Component", + value="Salary" + ), + DocGenFormField( + name="Details", + value=f"${args['salary']}" + ) + ] + ), + DocGenFormFieldRowValue( + doc_gen_form_field_list=[ + DocGenFormField( + name="Compensation_Component", + value="Bonus" + ), + DocGenFormField( + name="Details", + value=bonus_value + ) + ] + ), + DocGenFormFieldRowValue( + doc_gen_form_field_list=[ + DocGenFormField( + name="Compensation_Component", + value="RSUs" + ), + DocGenFormField( + name="Details", + value=args["rsus"] + ) + ] + ) + ] + ) + ] + ) + ] + ) + return doc_gen_form_field_request + #ds-snippet-end:eSign42Step7 diff --git a/app/eSignature/examples/eg043_shared_access.py b/app/eSignature/examples/eg043_shared_access.py new file mode 100644 index 00000000..66d68f58 --- /dev/null +++ b/app/eSignature/examples/eg043_shared_access.py @@ -0,0 +1,94 @@ +import json + +from docusign_esign import EnvelopesApi, UsersApi, AccountsApi, NewUsersDefinition, UserInformation, \ + UserAuthorizationCreateRequest, AuthorizationUser, ApiException +from datetime import datetime, timedelta + +from ...docusign import create_api_client + + +class Eg043SharedAccessController: + @classmethod + def create_agent(cls, args): + #ds-snippet-start:eSign43Step2 + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + #ds-snippet-end:eSign43Step2 + #ds-snippet-start:eSign43Step3 + users_api = UsersApi(api_client) + #ds-snippet-end:eSign43Step3 + + # check if agent already exists + try: + users = users_api.list(args["account_id"], email=args["email"], status="Active") + if int(users.result_set_size) > 0: + return users.users[0] + + except ApiException as err: + error_body_json = err and hasattr(err, "body") and err.body + error_body = json.loads(error_body_json) + error_code = error_body and "errorCode" in error_body and error_body["errorCode"] + + user_not_found_error_codes = ["USER_NOT_FOUND", "USER_LACKS_MEMBERSHIP"] + if error_code not in user_not_found_error_codes: + raise err + + # create new agent + #ds-snippet-start:eSign43Step3 + new_users = users_api.create(args["account_id"], new_users_definition=cls.new_users_definition(args)) + return new_users.new_users[0] + #ds-snippet-end:eSign43Step3 + + #ds-snippet-start:eSign43Step4 + @classmethod + def create_authorization(cls, args): + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + accounts_api = AccountsApi(api_client) + + # check if authorization with manage permission already exists + authorizations = accounts_api.get_agent_user_authorizations( + args["account_id"], + args["agent_user_id"], + permissions="manage" + ) + if int(authorizations.result_set_size) > 0: + return + + # create authorization + return accounts_api.create_user_authorization( + args["account_id"], + args["user_id"], + user_authorization_create_request=cls.user_authorization_request(args) + ) + #ds-snippet-end:eSign43Step4 + + #ds-snippet-start:eSign43Step3 + @classmethod + def new_users_definition(cls, args): + agent = UserInformation( + user_name=args["user_name"], + email=args["email"], + activation_access_code=args["activation"] + ) + return NewUsersDefinition(new_users=[agent]) + #ds-snippet-end:eSign43Step3 + + @classmethod + def user_authorization_request(cls, args): + return UserAuthorizationCreateRequest( + agent_user=AuthorizationUser( + account_id=args["account_id"], + user_id=args["agent_user_id"] + ), + permission="manage" + ) + + #ds-snippet-start:eSign43Step5 + @classmethod + def get_envelopes(cls, args): + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + api_client.set_default_header("X-DocuSign-Act-On-Behalf", args["user_id"]) + envelopes_api = EnvelopesApi(api_client) + + from_date = (datetime.utcnow() - timedelta(days=10)).isoformat() + return envelopes_api.list_status_changes(account_id=args["account_id"], from_date=from_date) + #ds-snippet-end:eSign43Step5 diff --git a/app/eSignature/examples/eg044_focused_view.py b/app/eSignature/examples/eg044_focused_view.py new file mode 100644 index 00000000..f65837d6 --- /dev/null +++ b/app/eSignature/examples/eg044_focused_view.py @@ -0,0 +1,146 @@ +import base64 +from os import path + +from docusign_esign import EnvelopesApi, RecipientViewRequest, Document, Signer, EnvelopeDefinition, SignHere, Tabs, \ + Recipients +from flask import session, url_for, request + +from ...consts import authentication_method, demo_docs_path, pattern, signer_client_id +from ...docusign import create_api_client +from ...ds_config import DS_CONFIG + + +class Eg044FocusedViewController: + @staticmethod + def get_args(): + """Get request and session arguments""" + # More data validation would be a good idea here + # Strip anything other than characters listed + # 1. Parse request arguments + signer_email = pattern.sub("", request.form.get("signer_email")) + signer_name = pattern.sub("", request.form.get("signer_name")) + envelope_args = { + "signer_email": signer_email, + "signer_name": signer_name, + "signer_client_id": signer_client_id, + "ds_return_url": url_for("ds.ds_return", _external=True), + } + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_args": envelope_args + } + return args + + @classmethod + def worker(cls, args): + """ + 1. Create the envelope request object + 2. Send the envelope + 3. Create the Recipient View request object + 4. Obtain the recipient_view_url for the embedded signing + """ + #ds-snippet-start:eSign44Step3 + envelope_args = args["envelope_args"] + # 1. Create the envelope request object + envelope_definition = cls.make_envelope(envelope_args) + + # 2. call Envelopes::create API method + # Exceptions will be caught by the calling function + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + + envelope_api = EnvelopesApi(api_client) + results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + + envelope_id = results.envelope_id + #ds-snippet-end:eSign44Step3 + + # 3. Create the Recipient View request object + #ds-snippet-start:eSign44Step4 + recipient_view_request = RecipientViewRequest( + authentication_method=authentication_method, + client_user_id=envelope_args["signer_client_id"], + recipient_id="1", + return_url=envelope_args["ds_return_url"], + user_name=envelope_args["signer_name"], + email=envelope_args["signer_email"], + frame_ancestors=["http://localhost:3000", "https://apps-d.docusign.com"], + message_origins=["https://apps-d.docusign.com"] + ) + #ds-snippet-end:eSign44Step4 + + # 4. Obtain the recipient_view_url for the embedded signing + # Exceptions will be caught by the calling function + + #ds-snippet-start:eSign44Step5 + results = envelope_api.create_recipient_view( + account_id=args["account_id"], + envelope_id=envelope_id, + recipient_view_request=recipient_view_request + ) + + return {"envelope_id": envelope_id, "redirect_url": results.url} + #ds-snippet-end:eSign44Step5 + + @classmethod + #ds-snippet-start:eSign44Step2 + def make_envelope(cls, args): + """ + Creates envelope + args -- parameters for the envelope: + signer_email, signer_name, signer_client_id + returns an envelope definition + """ + + # document 1 (pdf) has tag /sn1/ + # + # The envelope has one recipient. + # recipient 1 - signer + with open(path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), "rb") as file: + content_bytes = file.read() + base64_file_content = base64.b64encode(content_bytes).decode("ascii") + + # Create the document model + document = Document( # create the DocuSign document object + document_base64=base64_file_content, + name="Example document", # can be different from actual file name + file_extension="pdf", # many different document types are accepted + document_id=1 # a label used to reference the doc + ) + + # Create the signer recipient model + signer = Signer( + # The signer + email=args["signer_email"], + name=args["signer_name"], + recipient_id="1", + routing_order="1", + # Setting the client_user_id marks the signer as embedded + client_user_id=args["signer_client_id"] + ) + + # Create a sign_here tab (field on the document) + sign_here = SignHere( + # DocuSign SignHere field/tab + anchor_string="/sn1/", + anchor_units="pixels", + anchor_y_offset="10", + anchor_x_offset="20" + ) + + # Add the tabs model (including the sign_here tab) to the signer + # The Tabs object wants arrays of the different field/tab types + signer.tabs = Tabs(sign_here_tabs=[sign_here]) + + # Next, create the top level envelope definition and populate it. + envelope_definition = EnvelopeDefinition( + email_subject="Please sign this document sent from the Python SDK", + documents=[document], + # The Recipients object wants arrays for each recipient type + recipients=Recipients(signers=[signer]), + status="sent" # requests that the envelope be created and sent. + ) + + return envelope_definition + #ds-snippet-end:eSign44Step2 diff --git a/app/eSignature/examples/eg045_delete_restore_envelope.py b/app/eSignature/examples/eg045_delete_restore_envelope.py new file mode 100644 index 00000000..c7def3b6 --- /dev/null +++ b/app/eSignature/examples/eg045_delete_restore_envelope.py @@ -0,0 +1,48 @@ +from docusign_esign import FoldersApi, FoldersRequest + +from ...docusign import create_api_client + + +class Eg045DeleteRestoreEnvelopeController: + @staticmethod + def delete_envelope(args): + #ds-snippet-start:eSign45Step2 + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + folders_api = FoldersApi(api_client) + #ds-snippet-end:eSign45Step2 + + #ds-snippet-start:eSign45Step3 + folders_request = FoldersRequest( + envelope_ids=[args["envelope_id"]] + ) + #ds-snippet-end:eSign45Step3 + + #ds-snippet-start:eSign45Step4 + results = folders_api.move_envelopes(account_id=args["account_id"], folder_id=args["delete_folder_id"], folders_request=folders_request) + #ds-snippet-end:eSign45Step4 + return results + + @staticmethod + def move_envelope_to_folder(args): + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + folders_api = FoldersApi(api_client) + + #ds-snippet-start:eSign45Step6 + folders_request = FoldersRequest( + envelope_ids=[args["envelope_id"]], + from_folder_id=args["from_folder_id"] + ) + + results = folders_api.move_envelopes(account_id=args["account_id"], folder_id=args["folder_id"], folders_request=folders_request) + #ds-snippet-end:eSign45Step6 + return results + + @staticmethod + def get_folders(args): + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + folders_api = FoldersApi(api_client) + + #ds-snippet-start:eSign45Step5 + results = folders_api.list(account_id=args["account_id"]) + #ds-snippet-end:eSign45Step5 + return results diff --git a/app/eSignature/utils.py b/app/eSignature/utils.py new file mode 100644 index 00000000..b1d72d79 --- /dev/null +++ b/app/eSignature/utils.py @@ -0,0 +1,10 @@ +def get_folder_id_by_name(folders, folder_name): + for folder in folders: + if folder.name.lower() == folder_name.lower(): + return folder.folder_id + + subfolders = folder.folders + if subfolders is not None and len(subfolders) > 0: + folder_id = get_folder_id_by_name(subfolders, folder_name) + if folder_id is not None: + return folder_id \ No newline at end of file diff --git a/app/eSignature/examples/__init__.py b/app/eSignature/views/__init__.py similarity index 73% rename from app/eSignature/examples/__init__.py rename to app/eSignature/views/__init__.py index 7a3f5718..00c5cc90 100644 --- a/app/eSignature/examples/__init__.py +++ b/app/eSignature/views/__init__.py @@ -17,8 +17,7 @@ from .eg017_set_template_tab_values import eg017 from .eg018_envelope_custom_field_data import eg018 from .eg019_access_code_authentication import eg019 -from .eg020_sms_authentication import eg020 -from .eg021_phone_authentication import eg021 +from .eg020_phone_authentication import eg020 from .eg022_kba_authentication import eg022 from .eg023_idv_authentication import eg023 from .eg024_permissions_creating import eg024 @@ -32,4 +31,14 @@ from .eg032_pause_signature_workflow import eg032 from .eg033_unpause_signature_workflow import eg033 from .eg034_use_conditional_recipients import eg034 -from .eg035_sms_delivery import eg035 +from .eg035_scheduled_sending import eg035 +from .eg036_delayed_routing import eg036 +from .eg037_sms_delivery import eg037 +from .eg038_responsive_signing import eg038 +from .eg039_in_person_signer import eg039 +from .eg040_document_visibility import eg040 +from .eg041_cfr_embedded_signing import eg041 +from .eg042_document_generation import eg042 +from .eg043_shared_access import eg043 +from .eg044_focused_view import eg044 +from .eg045_delete_restore_envelope import eg045 diff --git a/app/eSignature/views/eg002_signing_via_email.py b/app/eSignature/views/eg002_signing_via_email.py new file mode 100644 index 00000000..e1a08a28 --- /dev/null +++ b/app/eSignature/views/eg002_signing_via_email.py @@ -0,0 +1,89 @@ +""" Example 002: Remote signer, cc, envelope has three documents """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint, request + +from ..examples.eg002_signing_via_email import Eg002SigningViaEmailController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import pattern, API_TYPE + +example_number = 2 +api = API_TYPE["ESIGNATURE"] +eg = f"eg00{example_number}" # reference (and url) for this example +eg002 = Blueprint(eg, __name__) + +def get_args(): + """Get request and session arguments""" + + # More data validation would be a good idea here + # Strip anything other than characters listed + signer_email = pattern.sub("", request.form.get("signer_email")) + signer_name = pattern.sub("", request.form.get("signer_name")) + cc_email = pattern.sub("", request.form.get("cc_email")) + cc_name = pattern.sub("", request.form.get("cc_name")) + envelope_args = { + "signer_email": signer_email, + "signer_name": signer_name, + "cc_email": cc_email, + "cc_name": cc_name, + "status": "sent", + } + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_args": envelope_args + } + return args + +@eg002.route(f"/{eg}", methods=["POST"]) +@authenticate(eg=eg, api=api) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +def sign_by_email(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response with envelopeId + """ + + # 1. Get required arguments + #args = Eg002SigningViaEmailController.get_args() + args = get_args() + try: + # 1. Call the worker method + results = Eg002SigningViaEmailController.worker(args, DS_CONFIG["doc_docx"], DS_CONFIG["doc_pdf"]) + except ApiException as err: + return process_error(err) + + session["envelope_id"] = results["envelope_id"] # Save for use by other examples which need an envelopeId + + # 2. Render success response with envelopeId + example = get_example_by_number(session["manifest"], example_number, api) + return render_template( + "example_done.html", + title=example["ExampleName"], + message=example["ResultsPageText"].format(results['envelope_id']) + ) + +@eg002.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg002_signing_via_email.html", + title=example["ExampleName"], + example=example, + source_file="eg002_signing_via_email.py", + source_url=DS_CONFIG["github_example_url"] + "eg002_signing_via_email.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg003_list_envelopes.py b/app/eSignature/views/eg003_list_envelopes.py new file mode 100644 index 00000000..fdd79f9b --- /dev/null +++ b/app/eSignature/views/eg003_list_envelopes.py @@ -0,0 +1,63 @@ +"""Example 003: List envelopes in the user"s account""" + +import json +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, Blueprint, session + +from ..examples.eg003_list_envelopes import Eg003ListEnvelopesController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 3 +api = API_TYPE["ESIGNATURE"] +eg = f"eg00{example_number}" # reference (and url) for this example +eg003 = Blueprint(eg, __name__) + + +@eg003.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def envelope_list(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Show envelopes + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg003ListEnvelopesController.get_args() + try: + # 1. Call the worker method + results = Eg003ListEnvelopesController.worker(args) + except ApiException as err: + return process_error(err) + # 3. Show envelopes + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from the Envelopes::listStatusChanges method:", + json=json.dumps(json.dumps(results.to_dict())) + ) + + +@eg003.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg003_list_envelopes.html", + title=example["ExampleName"], + example=example, + source_file= "eg003_list_envelopes.py", + source_url=DS_CONFIG["github_example_url"] + "eg003_list_envelopes.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) diff --git a/app/eSignature/views/eg004_envelope_info.py b/app/eSignature/views/eg004_envelope_info.py new file mode 100644 index 00000000..7babe310 --- /dev/null +++ b/app/eSignature/views/eg004_envelope_info.py @@ -0,0 +1,75 @@ +"""004: Get an envelope"s basic information and status""" + +import json +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint + +from ..examples.eg004_envelope_info import Eg004EnvelopeInfoController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 4 +api = API_TYPE["ESIGNATURE"] +eg = f"eg00{example_number}" # reference (and url) for this example +eg004 = Blueprint(eg, __name__) + + +@eg004.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def envelope_info(): + """ + 1. Get required arguments + 1. Call the worker method + 2. Show envelope info + """ + example = get_example_by_number(session["manifest"], example_number, api) + + if "envelope_id" in session: + # 1. Get required arguments + args = Eg004EnvelopeInfoController.get_args() + try: + # 1. Call the worker method + results = Eg004EnvelopeInfoController.worker(args) + except ApiException as err: + return process_error(err) + # 2.Show envelope info + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from the Envelopes::get method:", + json=json.dumps(json.dumps(results.to_dict())) + ) + else: + return render_template( + "eSignature/eg004_envelope_info.html", + title=example["ExampleName"], + envelope_ok=False, + source_file= "eg004_envelope_info.py", + source_url=DS_CONFIG["github_example_url"] + "eg004_envelope_info.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) + + +@eg004.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg004_envelope_info.html", + title=example["ExampleName"], + example=example, + envelope_ok="envelope_id" in session, + source_file= "eg004_envelope_info.py", + source_url=DS_CONFIG["github_example_url"] + "eg004_envelope_info.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) diff --git a/app/eSignature/views/eg005_envelope_recipients.py b/app/eSignature/views/eg005_envelope_recipients.py new file mode 100644 index 00000000..f1d68ff0 --- /dev/null +++ b/app/eSignature/views/eg005_envelope_recipients.py @@ -0,0 +1,76 @@ +"""005: List an envelope"s recipients and status""" + +import json +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint, session + +from ..examples.eg005_envelope_recipients import Eg005EnvelopeRecipientsController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 5 +api = API_TYPE["ESIGNATURE"] +eg = f"eg00{example_number}" # reference (and url) for this example +eg005 = Blueprint(eg, __name__) + + +@eg005.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def envelope_recipients(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Show recipients + """ + example = get_example_by_number(session["manifest"], example_number, api) + + if "envelope_id" in session: + # 1. Get required arguments + args = Eg005EnvelopeRecipientsController.get_args() + try: + # 2. Call the worker method + results = Eg005EnvelopeRecipientsController.worker(args) + except ApiException as err: + return process_error(err) + # 3. Show recipients + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from the EnvelopesRecipients::list method:", + json=json.dumps(json.dumps(results.to_dict())) + ) + else: + return render_template( + "eSignature/eg005_envelope_recipients.html", + title=example["ExampleName"], + example=example, + envelope_ok=False, + source_file= "eg005_envelope_recipients.py", + source_url=DS_CONFIG["github_example_url"] + "eg005_envelope_recipients.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) + + +@eg005.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg005_envelope_recipients.html", + title=example["ExampleName"], + example=example, + envelope_ok="envelope_id" in session, + source_file= "eg005_envelope_recipients.py", + source_url=DS_CONFIG["github_example_url"] + "eg005_envelope_recipients.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) diff --git a/app/eSignature/views/eg006_envelope_docs.py b/app/eSignature/views/eg006_envelope_docs.py new file mode 100644 index 00000000..cf9e4aca --- /dev/null +++ b/app/eSignature/views/eg006_envelope_docs.py @@ -0,0 +1,82 @@ +"""006: List an envelope"s documents""" +import json +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint + +from ..examples.eg006_envelope_docs import Eg006EnvelopeDocsController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 6 +api = API_TYPE["ESIGNATURE"] +eg = f"eg00{example_number}" # reference (and url) for this example +eg006 = Blueprint(eg, __name__) + + +@eg006.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def envelope_docs(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Save envelope documents + 4. Show envelope documents + """ + example = get_example_by_number(session["manifest"], example_number, api) + + if "envelope_id" in session: + # 1. Get required arguments + args = Eg006EnvelopeDocsController.get_args() + try: + # 2. Call the worker method + results = Eg006EnvelopeDocsController.worker(args) + except ApiException as err: + return process_error(err) + + # 3. Save envelope documents + Eg006EnvelopeDocsController.save_envelope_documents(results) + + # 4. Show envelope documents + return render_template( + "example_done.html", + title="List an envelope's documents", + h1="List an envelope's documents", + message="Results from the EnvelopeDocuments::list method:", + json=json.dumps(json.dumps(results.to_dict())) + ) + + else: + return render_template( + "eSignature/eg006_envelope_docs.html", + title=example["ExampleName"], + example=example, + envelope_ok=False, + source_file= "eg006_envelope_docs.py", + source_url=DS_CONFIG["github_example_url"] + "eg006_envelope_docs.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) + + +@eg006.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg006_envelope_docs.html", + title=example["ExampleName"], + example=example, + envelope_ok="envelope_id" in session, + source_file= "eg006_envelope_docs.py", + source_url=DS_CONFIG["github_example_url"] + "eg006_envelope_docs.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) diff --git a/app/eSignature/examples/eg007_envelope_get_doc/views.py b/app/eSignature/views/eg007_envelope_get_doc.py similarity index 52% rename from app/eSignature/examples/eg007_envelope_get_doc/views.py rename to app/eSignature/views/eg007_envelope_get_doc.py index 087e2286..48ce12e9 100644 --- a/app/eSignature/examples/eg007_envelope_get_doc/views.py +++ b/app/eSignature/views/eg007_envelope_get_doc.py @@ -5,34 +5,39 @@ from docusign_esign.client.api_exception import ApiException from flask import render_template, session, send_file, Blueprint -from .controller import Eg007Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error +from ..examples.eg007_envelope_get_doc import Eg007EnvelopeGetDocController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE -eg = "eg007" # reference (and url) for this example -eg007 = Blueprint("eg007", __name__) +example_number = 7 +api = API_TYPE["ESIGNATURE"] +eg = f"eg00{example_number}" # reference (and url) for this example +eg007 = Blueprint(eg, __name__) -@eg007.route("/eg007", methods=["POST"]) -@authenticate(eg=eg) +@eg007.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def get_envelope_doc(): """ 1. Get required arguments 2. Call the worker method 3. Download envelope document """ + example = get_example_by_number(session["manifest"], example_number, api) if "envelope_id" in session and "envelope_documents" in session: # 1. Get required arguments - args = Eg007Controller.get_args() + args = Eg007EnvelopeGetDocController.get_args() try: # 2. Call the worker method - results = Eg007Controller.worker(args) + results = Eg007EnvelopeGetDocController.worker(args) except ApiException as err: return process_error(err) - # 3. Download envelope document + # 3. Download envelope document from the temp file path return send_file( results["data"], mimetype=results["mimetype"], @@ -41,21 +46,24 @@ def get_envelope_doc(): ) else: return render_template( - "eg007_envelope_get_doc.html", - title="Download an Envelope's Document", + "eSignature/eg007_envelope_get_doc.html", + title=example["ExampleName"], + example=example, envelope_ok=False, documents_ok=False, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", + source_file= "eg007_envelope_get_doc.py", + source_url=DS_CONFIG["github_example_url"] + "eg007_envelope_get_doc.py", documentation=DS_CONFIG["documentation"] + eg, show_doc=DS_CONFIG["documentation"], ) -@eg007.route("/eg007", methods=["GET"]) -@authenticate(eg=eg) +@eg007.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def get_view(): """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) documents_ok = "envelope_documents" in session document_options = [] @@ -67,12 +75,13 @@ def get_view(): , envelope_documents["documents"]) return render_template( - "eg007_envelope_get_doc.html", - title="Download an Envelope's Document", + "eSignature/eg007_envelope_get_doc.html", + title=example["ExampleName"], + example=example, envelope_ok="envelope_id" in session, documents_ok=documents_ok, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", + source_file= "eg007_envelope_get_doc.py", + source_url=DS_CONFIG["github_example_url"] + "eg007_envelope_get_doc.py", documentation=DS_CONFIG["documentation"] + eg, show_doc=DS_CONFIG["documentation"], document_options=document_options diff --git a/app/eSignature/examples/eg008_create_template/views.py b/app/eSignature/views/eg008_create_template.py similarity index 52% rename from app/eSignature/examples/eg008_create_template/views.py rename to app/eSignature/views/eg008_create_template.py index e7428295..f1f236f3 100644 --- a/app/eSignature/examples/eg008_create_template/views.py +++ b/app/eSignature/views/eg008_create_template.py @@ -5,29 +5,34 @@ from docusign_esign.client.api_exception import ApiException from flask import render_template, session, Blueprint -from .controller import Eg008Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error +from ..examples.eg008_create_template import Eg008CreateTemplateController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE -eg = "eg008" # reference (and url) for this example -eg008 = Blueprint("eg008", __name__) +example_number = 8 +api = API_TYPE["ESIGNATURE"] +eg = f"eg00{example_number}" # reference (and url) for this example +eg008 = Blueprint(eg, __name__) -@eg008.route("/eg008", methods=["POST"]) -@authenticate(eg=eg) +@eg008.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def create_template(): """ 1. Get required arguments 2. Call the worker method 3. Render template info """ + example = get_example_by_number(session["manifest"], example_number, api) # 1. Get required arguments - args = Eg008Controller.get_args() + args = Eg008CreateTemplateController.get_args() try: # 2. Call the worker method - results = Eg008Controller.worker(args) + results = Eg008CreateTemplateController.worker(args) except ApiException as err: return process_error(err) @@ -47,15 +52,18 @@ def create_template(): ) -@eg008.route("/eg008", methods=["GET"]) +@eg008.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) def get_view(): """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) return render_template( - "eg008_create_template.html", - title="Create a template", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", + "eSignature/eg008_create_template.html", + title=example["ExampleName"], + example=example, + source_file= "eg008_create_template.py", + source_url=DS_CONFIG["github_example_url"] + "eg008_create_template.py", documentation=DS_CONFIG["documentation"] + eg, show_doc=DS_CONFIG["documentation"], ) diff --git a/app/eSignature/examples/eg009_use_template/views.py b/app/eSignature/views/eg009_use_template.py similarity index 51% rename from app/eSignature/examples/eg009_use_template/views.py rename to app/eSignature/views/eg009_use_template.py index 1784ba1c..1cb0b171 100644 --- a/app/eSignature/examples/eg009_use_template/views.py +++ b/app/eSignature/views/eg009_use_template.py @@ -5,27 +5,33 @@ from docusign_esign.client.api_exception import ApiException from flask import render_template, session, Blueprint -from .controller import Eg009Controller -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error +from ...docusign import ensure_manifest, get_example_by_number +from ..examples.eg009_use_template import Eg009UseTemplateController +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE -eg = "eg009" # reference (and url) for this example -eg009 = Blueprint("eg009", __name__) +example_number = 9 +api = API_TYPE["ESIGNATURE"] +eg = f"eg00{example_number}" # reference (and url) for this example +eg009 = Blueprint(eg, __name__) -@eg009.route("/eg009", methods=["POST"]) +@eg009.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) def use_template(): """ 1. 1. Get required arguments 2. Call the worker method """ + example = get_example_by_number(session["manifest"], example_number, api) if "template_id" in session: # 1. Get required arguments - args = Eg009Controller.get_args() + args = Eg009UseTemplateController.get_args() try: # 1. Call the worker method - results = Eg009Controller.worker(args) + results = Eg009UseTemplateController.worker(args) except ApiException as err: return process_error(err) @@ -41,26 +47,30 @@ def use_template(): ) else: return render_template( - "eg009_use_template.html", - title="Use a template to send an envelope", + "eSignature/eg009_use_template.html", + title=example["ExampleName"], + example=example, template_ok=False, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", + source_file= "eg009_use_template.py", + source_url=DS_CONFIG["github_example_url"] + "eg009_use_template.py", documentation=DS_CONFIG["documentation"] + eg, show_doc=DS_CONFIG["documentation"], ) -@eg009.route("/eg009", methods=["GET"]) +@eg009.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) def get_view(): """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) return render_template( - "eg009_use_template.html", - title="Use a template to send an envelope", + "eSignature/eg009_use_template.html", + title=example["ExampleName"], + example=example, template_ok="template_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", + source_file= "eg009_use_template.py", + source_url=DS_CONFIG["github_example_url"] + "eg009_use_template.py", documentation=DS_CONFIG["documentation"] + eg, show_doc=DS_CONFIG["documentation"], signer_name=DS_CONFIG["signer_name"], diff --git a/app/eSignature/examples/eg010_send_binary_docs/views.py b/app/eSignature/views/eg010_send_binary_docs.py similarity index 56% rename from app/eSignature/examples/eg010_send_binary_docs/views.py rename to app/eSignature/views/eg010_send_binary_docs.py index b3559fce..d0c04dd8 100644 --- a/app/eSignature/examples/eg010_send_binary_docs/views.py +++ b/app/eSignature/views/eg010_send_binary_docs.py @@ -2,29 +2,34 @@ from os import path -from flask import render_template, Blueprint +from flask import render_template, Blueprint, session -from .controller import Eg010Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG +from ..examples.eg010_send_binary_docs import Eg010SendBinaryDocsController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...consts import API_TYPE -eg = "eg010" # reference (and url) for this example -eg010 = Blueprint("eg010", __name__) +example_number = 10 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg010 = Blueprint(eg, __name__) -@eg010.route("/eg010", methods=["POST"]) -@authenticate(eg=eg) +@eg010.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def send_bynary_docs(): """ 1. Get required arguments 2. Call the worker method 3. Render success response """ + example = get_example_by_number(session["manifest"], example_number, api) # 1. Get required arguments - args = Eg010Controller.get_args() + args = Eg010SendBinaryDocsController.get_args() # 2. Call the worker method - results = Eg010Controller.worker(args) + results = Eg010SendBinaryDocsController.worker(args) if results["status_code"] < 299: # 3. Render Success response @@ -50,16 +55,19 @@ def send_bynary_docs(): ) -@eg010.route("/eg010", methods=["GET"]) -@authenticate(eg=eg) +@eg010.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def get_view(): """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) return render_template( - "eg010_send_binary_docs.html", - title="Send binary documents", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", + "eSignature/eg010_send_binary_docs.html", + title=example["ExampleName"], + example=example, + source_file= "eg010_send_binary_docs.py", + source_url=DS_CONFIG["github_example_url"] + "eg010_send_binary_docs.py", documentation=DS_CONFIG["documentation"] + eg, show_doc=DS_CONFIG["documentation"], signer_name=DS_CONFIG["signer_name"], diff --git a/app/eSignature/views/eg011_embedded_sending.py b/app/eSignature/views/eg011_embedded_sending.py new file mode 100644 index 00000000..3b91c938 --- /dev/null +++ b/app/eSignature/views/eg011_embedded_sending.py @@ -0,0 +1,62 @@ +"""011: Embedded sending: Remote signer, cc, envelope has three documents""" + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, redirect, Blueprint, session + +from ..examples.eg011_embedded_sending import Eg011EmbeddedSendingController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 11 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg011 = Blueprint(eg, __name__) + + +@eg011.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def embedded_sending(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Redirect user to NDSE view + """ + + # 1. Get required arguments + args = Eg011EmbeddedSendingController.get_args() + try: + # 2. Call the worker method + results = Eg011EmbeddedSendingController.worker(args, DS_CONFIG["doc_docx"], DS_CONFIG["doc_pdf"]) + except ApiException as err: + return process_error(err) + + # Redirect the user to the NDSE view + # Don"t use an iFrame! + # State can be stored/recovered using the framework"s session or a + # query parameter on the returnUrl (see the makeRecipientViewRequest method) + return redirect(results["redirect_url"]) + + +@eg011.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg011_embedded_sending.html", + title=example["ExampleName"], + example=example, + source_file= "eg011_embedded_sending.py", + source_url=DS_CONFIG["github_example_url"] + "eg011_embedded_sending.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg012_embedded_console.py b/app/eSignature/views/eg012_embedded_console.py new file mode 100644 index 00000000..dcc551cb --- /dev/null +++ b/app/eSignature/views/eg012_embedded_console.py @@ -0,0 +1,61 @@ +"""012: Embedded console""" + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, redirect, session, Blueprint + +from ..examples.eg012_embedded_console import Eg012EmbeddedConsoleController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 12 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg012 = Blueprint(eg, __name__) + + +@eg012.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def embedded_console(): + """ + 1. Get required args + 2. Call the worker method + 3. Redirect user to NDSE view + """ + + # 1. Get required args + args = Eg012EmbeddedConsoleController.get_args() + try: + # 2. Call the worker method + results = Eg012EmbeddedConsoleController.worker(args) + except ApiException as err: + return process_error(err) + + # 3. Redirect the user to the NDSE view + # Don"t use an iFrame! + # State can be stored/recovered using the framework"s session + return redirect(results["redirect_url"]) + + +@eg012.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + envelope_id = "envelope_id" in session and session["envelope_id"] + return render_template( + "eSignature/eg012_embedded_console.html", + title=example["ExampleName"], + example=example, + envelope_ok=envelope_id, + source_file= "eg012_embedded_console.py", + source_url=DS_CONFIG["github_example_url"] + "eg012_embedded_console.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) diff --git a/app/eSignature/views/eg013_add_doc_to_template.py b/app/eSignature/views/eg013_add_doc_to_template.py new file mode 100644 index 00000000..7f42b05b --- /dev/null +++ b/app/eSignature/views/eg013_add_doc_to_template.py @@ -0,0 +1,77 @@ +"""Example 013: Embedded Signing from template with added document""" + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, redirect, session, Blueprint + +from ..examples.eg013_add_doc_to_template import Eg013AddDocToTemplateController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 13 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg013 = Blueprint(eg, __name__) + + +@eg013.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def add_doc_template(): + """ + 1. Check the presence of a saved template_id + 2. Get required arguments + 3. Call the worker method + 4. Redirect user to Signing ceremory + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Check the presence of a saved template_id + if "template_id" in session: + # 2. Get required arguments + args = Eg013AddDocToTemplateController.get_args() + try: + # 3. Call the worker method + results = Eg013AddDocToTemplateController.worker(args) + except ApiException as err: + return process_error(err) + + # 4. Redirect the user to the embedded signing + # Don"t use an iFrame! + # State can be stored/recovered using the framework"s session + return redirect(results["redirect_url"]) + + else: + return render_template( + "eSignature/eg013_add_doc_to_template.html", + title=example["ExampleName"], + template_ok=False, + source_file="eg013_add_doc_to_template.py", + source_url=DS_CONFIG["github_example_url"] + "eg013_add_doc_to_template.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) + + +@eg013.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg013_add_doc_to_template.html", + title=example["ExampleName"], + example=example, + template_ok="template_id" in session, + source_file="eg013_add_doc_to_template.py", + source_url=DS_CONFIG["github_example_url"] + "eg013_add_doc_to_template.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg014_collect_payment.py b/app/eSignature/views/eg014_collect_payment.py new file mode 100644 index 00000000..88856271 --- /dev/null +++ b/app/eSignature/views/eg014_collect_payment.py @@ -0,0 +1,71 @@ +""" Example 014: Remote signer, cc; envelope has an order form """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, Blueprint, session + +from ..examples.eg014_collect_payment import Eg014CollectPaymentController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 14 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg014 = Blueprint(eg, __name__) + + +@eg014.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def collect_payment(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg014CollectPaymentController.get_args() + try: + # 2. Call the worker method + results = Eg014CollectPaymentController.worker(args) + except ApiException as err: + return process_error(err) + + # 3. Render success response + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"""The envelope has been created and sent!
Envelope ID {results["envelope_id"]}.""" + ) + + +@eg014.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + gateway = DS_CONFIG["gateway_account_id"] + gateway_ok = gateway and len(gateway) > 25 + + if "is_cfr" in session and session["is_cfr"] == "enabled": + return render_template("cfr_error.html", title="Error") + + return render_template( + "eSignature/eg014_collect_payment.html", + title=example["ExampleName"], + example=example, + source_file= "eg014_collect_payment.py", + source_url=DS_CONFIG["github_example_url"] + "eg014_collect_payment.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"], + gateway_ok=gateway_ok + ) diff --git a/app/eSignature/views/eg015_envelope_tab_data.py b/app/eSignature/views/eg015_envelope_tab_data.py new file mode 100644 index 00000000..3e8d766c --- /dev/null +++ b/app/eSignature/views/eg015_envelope_tab_data.py @@ -0,0 +1,82 @@ +"""015: Get an envelope"s tab information data""" + +import json +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint + +from ..examples.eg015_envelope_tab_data import Eg015EnvelopeTabDateController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 15 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # Reference (and URL) for this example +eg015 = Blueprint(eg, __name__) + + +@eg015.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def envelope_tab_data(): + """ + 1. Check presence of envelope_id in session + 2. Get required arguments + 3. Call the worker method + 4. Show Envelope tab data results + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Check presence of envelope_id in session + if "envelope_id" in session: + + # 2. Get required arguments + args = Eg015EnvelopeTabDateController.get_args() + + try: + # 3. Call the worker method + results = Eg015EnvelopeTabDateController.worker(args) + except ApiException as err: + return process_error(err) + + # 4.Show Envelope tab data results + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from the Envelopes::formData GET method:", + json=json.dumps(json.dumps(results.to_dict())) + ) + + else: + return render_template( + "eSignature/eg015_envelope_tab_data.html", + title=example["ExampleName"], + example=example, + envelope_ok=False, + source_file= "eg015_envelope_tab_data.py", + source_url=DS_CONFIG["github_example_url"] + "eg015_envelope_tab_data.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) + + +@eg015.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg015_envelope_tab_data.html", + title=example["ExampleName"], + example=example, + envelope_ok="envelope_id" in session, + source_file= "eg015_envelope_tab_data.py", + source_url=DS_CONFIG["github_example_url"] + "eg015_envelope_tab_data.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) diff --git a/app/eSignature/views/eg016_set_tab_values.py b/app/eSignature/views/eg016_set_tab_values.py new file mode 100644 index 00000000..e4596b41 --- /dev/null +++ b/app/eSignature/views/eg016_set_tab_values.py @@ -0,0 +1,70 @@ +"""Example 016: Set Tab Values""" + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, redirect, session, Blueprint, current_app + +from ..examples.eg016_set_tab_values import Eg016SetTabValuesController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 16 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # Reference (and URL) for this example +eg016 = Blueprint(eg, __name__) + + +# the signer? See the "authenticationMethod" definition +# https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopeviews/createrecipient/ + + +@eg016.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def set_tab_values(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Redirect the user to the embedded signing + """ + + # 1. Get required arguments + args = Eg016SetTabValuesController.get_args() + + try: + # 2. Call the worker method for setting tab values + results = Eg016SetTabValuesController.worker(args) + current_app.logger.info(f"Envelope was created. EnvelopeId {results['envelope_id']}") + except ApiException as err: + return process_error(err) + + session["envelope_id"] = results["envelope_id"] # Save for use by other examples + # that need an envelope ID + # 3. Redirect the user to the embedded signing + # Don"t use an iframe! + # State can be stored/recovered using the framework"s session or a + # query parameter on the return URL (see the makeRecipientViewRequest method) + return redirect(results["redirect_url"]) + + +@eg016.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg016_set_tab_values.html", + title=example["ExampleName"], + example=example, + source_file= "eg016_set_tab_values.py", + source_url=DS_CONFIG["github_example_url"] + "eg016_set_tab_values.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/examples/eg017_set_template_tab_values/views.py b/app/eSignature/views/eg017_set_template_tab_values.py similarity index 50% rename from app/eSignature/examples/eg017_set_template_tab_values/views.py rename to app/eSignature/views/eg017_set_template_tab_values.py index 000c7299..a3f2a1bc 100644 --- a/app/eSignature/examples/eg017_set_template_tab_values/views.py +++ b/app/eSignature/views/eg017_set_template_tab_values.py @@ -5,21 +5,25 @@ from docusign_esign.client.api_exception import ApiException from flask import render_template, redirect, session, Blueprint -from .controller import Eg017Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error +from ..examples.eg017_set_template_tab_values import Eg017SetTemplateTabValuesController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE -eg = "eg017" # reference (and url) for this example -eg017 = Blueprint("eg017", __name__) +example_number = 17 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg017 = Blueprint(eg, __name__) # the signer? See the "authenticationMethod" definition -# https://developers.docusign.com/esign-rest-api/reference/Envelopes/EnvelopeViews/createRecipient +# https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopeviews/createrecipient/ -@eg017.route("/eg017", methods=["POST"]) -@authenticate(eg=eg) +@eg017.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def set_template_tab_values(): """ 1. Get required arguments @@ -28,11 +32,11 @@ def set_template_tab_values(): """ # 1. Get required arguments - args = Eg017Controller.get_args() + args = Eg017SetTemplateTabValuesController.get_args() try: # 2. Call the worker method for setting template tab values - results = Eg017Controller.worker(args) + results = Eg017SetTemplateTabValuesController.worker(args) except ApiException as err: return process_error(err) @@ -45,17 +49,20 @@ def set_template_tab_values(): return redirect(results["redirect_url"]) -@eg017.route("/eg017", methods=["GET"]) -@authenticate(eg=eg) +@eg017.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def get_view(): """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) return render_template( - "eg017_set_template_tab_values.html", - title="SetTemplateTabValues", + "eSignature/eg017_set_template_tab_values.html", + title=example["ExampleName"], + example=example, template_ok="template_id" in session, - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", + source_file= "eg017_set_template_tab_values.py", + source_url=DS_CONFIG["github_example_url"] + "eg017_set_template_tab_values.py", documentation=DS_CONFIG["documentation"] + eg, show_doc=DS_CONFIG["documentation"], signer_name=DS_CONFIG["signer_name"], diff --git a/app/eSignature/views/eg018_envelope_custom_field_data.py b/app/eSignature/views/eg018_envelope_custom_field_data.py new file mode 100644 index 00000000..e2fe657d --- /dev/null +++ b/app/eSignature/views/eg018_envelope_custom_field_data.py @@ -0,0 +1,77 @@ +"""018: Get an envelope"s custom field data""" + +import json +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint + +from ..examples.eg018_envelope_custom_field_data import Eg018EnvelopeCustomFieldDataController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 18 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and URL) for this example +eg018 = Blueprint(eg, __name__) + + +@eg018.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def envelope_custom_field_data(): + """ + 1. Get required args + 2. Call the worker method + 3. Show custom field data + """ + example = get_example_by_number(session["manifest"], example_number, api) + + if "envelope_id" in session: + # 1. Get required args + args = Eg018EnvelopeCustomFieldDataController.get_args() + + try: + # 2. Call the worker method for creating envelope with custom field + results = Eg018EnvelopeCustomFieldDataController.worker(args) + except ApiException as err: + return process_error(err) + # 3. Step render the results + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from the EnvelopeCustomFields::list method:", + json=json.dumps(json.dumps(results.to_dict())) + ) + else: + return render_template( + "eSignature/eg018_envelope_custom_field_data.html", + title=example["ExampleName"], + example=example, + envelope_ok=False, + source_file= "eg018_envelope_custom_field_data.py", + source_url=DS_CONFIG["github_example_url"] + "eg018_envelope_custom_field_data.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) + + +@eg018.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg015_envelope_tab_data.html", + title=example["ExampleName"], + example=example, + envelope_ok="envelope_id" in session, + source_file= "eg018_envelope_custom_field_data.py", + source_url=DS_CONFIG["github_example_url"] + "eg018_envelope_custom_field_data.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) diff --git a/app/eSignature/views/eg019_access_code_authentication.py b/app/eSignature/views/eg019_access_code_authentication.py new file mode 100644 index 00000000..400ee587 --- /dev/null +++ b/app/eSignature/views/eg019_access_code_authentication.py @@ -0,0 +1,66 @@ +""" Example 019: Access Code Recipient Authentication""" + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import current_app as app +from flask import render_template, Blueprint, session + +from ..examples.eg019_access_code_authentication import Eg019AccessCodeAuthenticationController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 19 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg019 = Blueprint(eg, __name__) + +@eg019.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def access_code_authentication(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + args = Eg019AccessCodeAuthenticationController.get_args() + try: + # Step 1: Call the worker method for authenticating with access code + results = Eg019AccessCodeAuthenticationController.worker(args) + envelope_id = results.envelope_id + + app.logger.info(f"Envelope was created. EnvelopeId {envelope_id} ") + + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"""The envelope has been created and sent!
Envelope ID {envelope_id}.""" + ) + + except ApiException as err: + return process_error(err) + + +@eg019.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg019_access_code_authentication.html", + title=example["ExampleName"], + example=example, + source_file= "eg019_access_code_authentication.py", + source_url=DS_CONFIG["github_example_url"] + "eg019_access_code_authentication.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg020_phone_authentication.py b/app/eSignature/views/eg020_phone_authentication.py new file mode 100644 index 00000000..4704f7b6 --- /dev/null +++ b/app/eSignature/views/eg020_phone_authentication.py @@ -0,0 +1,92 @@ +""" Example 020: Recipient Phone Authentication""" + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import current_app as app +from flask import render_template, Blueprint, session + +from ..examples.eg020_phone_authentication import Eg020PhoneAuthenticationController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...docusign.utils import is_cfr +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 20 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg020 = Blueprint(eg, __name__) + + +@eg020.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def phone_authentication(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg020PhoneAuthenticationController.get_args() + if args["envelope_args"]["signer_email"] == DS_CONFIG["signer_email"]: + return render_template( + "error.html", + error_code=400, + error_message=session["manifest"]["SupportingTexts"]["IdenticalEmailsNotAllowedErrorMessage"] + ) + + try: + # Step 2: Call the worker method for authenticating with phone + results = Eg020PhoneAuthenticationController.worker(args) + envelope_id = results.envelope_id + app.logger.info(f"Envelope was created. EnvelopeId {envelope_id} ") + + # 3. Render success response + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"""The envelope has been created and sent!
Envelope ID {envelope_id}.""" + ) + + except ApiException as err: + return process_error(err) + + +@eg020.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = { + "account_id": session["ds_account_id"], # represent your {ACCOUNT_ID} + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], # represent your {ACCESS_TOKEN} + } + + workflow_id = Eg020PhoneAuthenticationController.get_workflow(args) + + cfr_status = is_cfr(session["ds_access_token"], session["ds_account_id"], session["ds_base_path"]) + if cfr_status == "enabled": + if DS_CONFIG["quickstart"] == "true": + return redirect(url_for("eg041.get_view")) + else: + return render_template("cfr_error.html", title="Error") + + return render_template( + "eSignature/eg020_phone_authentication.html", + title=example["ExampleName"], + example=example, + source_file= "eg020_phone_authentication.py", + source_url=DS_CONFIG["github_example_url"] + "eg020_phone_authentication.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"], + workflow_id = workflow_id + ) diff --git a/app/eSignature/views/eg022_kba_authentication.py b/app/eSignature/views/eg022_kba_authentication.py new file mode 100644 index 00000000..a9d5e021 --- /dev/null +++ b/app/eSignature/views/eg022_kba_authentication.py @@ -0,0 +1,75 @@ +""" Example 022: Knowledge Based authentication""" + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import current_app as app +from flask import render_template, Blueprint, session + +from ..examples.eg022_kba_authentication import Eg022KBAAuthenticationController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 22 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg022 = Blueprint(eg, __name__) + + +@eg022.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def kba_authentication(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg022KBAAuthenticationController.get_args() + + if args["envelope_args"]["signer_email"] == DS_CONFIG["signer_email"]: + return render_template( + "error.html", + error_code=400, + error_message=session["manifest"]["SupportingTexts"]["IdenticalEmailsNotAllowedErrorMessage"] + ) + try: + # Step 2: Call the worker method for kba + results = Eg022KBAAuthenticationController.worker(args) + envelope_id = results.envelope_id + app.logger.info(f"Envelope was created. EnvelopeId {envelope_id} ") + + # 3. Render success response + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"""The envelope has been created and sent!
Envelope ID {envelope_id}.""" + ) + + except ApiException as err: + return process_error(err) + + +@eg022.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg022_kba_authentication.html", + title=example["ExampleName"], + example=example, + source_file= "eg022_kba_authentication.py", + source_url=DS_CONFIG["github_example_url"] + "eg022_kba_authentication.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg023_idv_authentication.py b/app/eSignature/views/eg023_idv_authentication.py new file mode 100644 index 00000000..b9dfc2a6 --- /dev/null +++ b/app/eSignature/views/eg023_idv_authentication.py @@ -0,0 +1,85 @@ +""" Example 023: ID Verificiation Based authentication""" + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import current_app as app +from flask import render_template, Blueprint, session + +from ..examples.eg023_idv_authentication import Eg023IDVAuthenticationController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 23 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # Reference (and URL) for this example +eg023 = Blueprint(eg, __name__) + + +@eg023.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def idv_authentication(): + """ + 1. Get required data + 2. Call the worker method + 3. Render success response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required data + args = Eg023IDVAuthenticationController.get_args() + + if args["envelope_args"]["signer_email"] == DS_CONFIG["signer_email"]: + return render_template( + "error.html", + error_code=400, + error_message=session["manifest"]["SupportingTexts"]["IdenticalEmailsNotAllowedErrorMessage"] + ) + try: + # 2: Call the worker method for idv authentication + results = Eg023IDVAuthenticationController.worker(args) + envelope_id = results.envelope_id + app.logger.info(f"Envelope was created. EnvelopeId {envelope_id} ") + + # 3. Render success response + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"The envelope has been created and sent!
Envelope ID {envelope_id}." + ) + + except ApiException as err: + return process_error(err) + + +@eg023.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = { + "account_id": session["ds_account_id"], # represent your {ACCOUNT_ID} + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], # represent your {ACCESS_TOKEN} + + } + + workflow_id = Eg023IDVAuthenticationController.get_workflow(args) + + return render_template( + "eSignature/eg023_idv_authentication.html", + title=example["ExampleName"], + example=example, + source_file= "eg023_idv_authentication.py", + source_url=DS_CONFIG["github_example_url"] + "eg023_idv_authentication.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"], + workflow_id=workflow_id + ) diff --git a/app/eSignature/views/eg024_permissions_creating.py b/app/eSignature/views/eg024_permissions_creating.py new file mode 100644 index 00000000..07ec4711 --- /dev/null +++ b/app/eSignature/views/eg024_permissions_creating.py @@ -0,0 +1,66 @@ +"""Example 024: Creating a permission profile""" + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import current_app as app +from flask import render_template, Blueprint, session + +from ..examples.eg024_permissions_creating import Eg024PermissionsCreatingController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 24 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" +eg024 = Blueprint(eg, __name__) + + +@eg024.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def permissions_creating(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render a response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required args + args = Eg024PermissionsCreatingController.get_args() + try: + # 2. Call the worker method to create a permission profile + response = Eg024PermissionsCreatingController.worker(args) + permission_profile_id = response.permission_profile_id + app.logger.info(f"The permission profile has been created. Permission profile ID: {permission_profile_id}") + + # 3. Render the response + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"""The permission profile has been created!
Permission profile ID: {permission_profile_id}.""" + ) + + except ApiException as err: + return process_error(err) + +@eg024.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg024_permissions_creating.html", + title=example["ExampleName"], + example=example, + source_file= "eg024_permissions_creating.py", + source_url=DS_CONFIG["github_example_url"] + "eg024_permissions_creating.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + permission_profile_name="Sample Profile 134972-Alpha" + ) \ No newline at end of file diff --git a/app/eSignature/examples/eg025_permissions_set_user_group/views.py b/app/eSignature/views/eg025_permissions_set_user_group.py similarity index 53% rename from app/eSignature/examples/eg025_permissions_set_user_group/views.py rename to app/eSignature/views/eg025_permissions_set_user_group.py index 59949fb4..2ba12d68 100644 --- a/app/eSignature/examples/eg025_permissions_set_user_group/views.py +++ b/app/eSignature/views/eg025_permissions_set_user_group.py @@ -4,28 +4,34 @@ from docusign_esign.client.api_exception import ApiException from flask import current_app as app from flask import render_template, session, request, Blueprint -from .controller import Eg025Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error -eg = "eg025" -eg025 = Blueprint("eg025", __name__) +from ..examples.eg025_permissions_set_user_group import Eg025PermissionsSetUserGroupController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE -@eg025.route("/eg025", methods=["POST"]) -@authenticate(eg=eg) +example_number = 25 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" +eg025 = Blueprint(eg, __name__) + +@eg025.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def permissions_set_user_group(): """ 1. Get required arguments 2. Call the worker method 3. Render a response """ + example = get_example_by_number(session["manifest"], example_number, api) # 1. Get required arguments - args = Eg025Controller.get_args() + args = Eg025PermissionsSetUserGroupController.get_args() try: # 2. Call the worker method to set the permission profile - response = Eg025Controller.worker(args) + response = Eg025PermissionsSetUserGroupController.worker(args) app.logger.info(f"The permission profile has been set.") permission_profile_id = response.groups[0].permission_profile_id group_id = response.groups[0].group_id @@ -33,8 +39,7 @@ def permissions_set_user_group(): # 3. Render the response return render_template( "example_done.html", - title="Set a permission profile for a group of users", - h1="Setting a permission profile for a group of users", + title=example["ExampleName"], message=f"""The permission profile has been set!
""" f"""Permission profile ID: {permission_profile_id}
""" f"""Group id: {group_id}""" @@ -43,22 +48,25 @@ def permissions_set_user_group(): except ApiException as err: return process_error(err) -@eg025.route("/eg025", methods=["GET"]) -@authenticate(eg=eg) +@eg025.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def get_view(): """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) args = { "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} "base_path": session["ds_base_path"], "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} } - permission_profiles, groups = Eg025Controller.get_data(args) + permission_profiles, groups = Eg025PermissionsSetUserGroupController.get_data(args) return render_template( - "eg025_permissions_set_user_group.html", - title="Setting a permission profile", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", + "eSignature/eg025_permissions_set_user_group.html", + title=example["ExampleName"], + example=example, + source_file= "eg025_permissions_set_user_group.py", + source_url=DS_CONFIG["github_example_url"] + "eg025_permissions_set_user_group.py", documentation=DS_CONFIG["documentation"] + eg, show_doc=DS_CONFIG["documentation"], permission_profiles=permission_profiles, diff --git a/app/eSignature/examples/eg026_permissions_change_single_setting/views.py b/app/eSignature/views/eg026_permissions_change_single_setting.py similarity index 52% rename from app/eSignature/examples/eg026_permissions_change_single_setting/views.py rename to app/eSignature/views/eg026_permissions_change_single_setting.py index 85c45903..48385775 100644 --- a/app/eSignature/examples/eg026_permissions_change_single_setting/views.py +++ b/app/eSignature/views/eg026_permissions_change_single_setting.py @@ -6,28 +6,33 @@ from flask import current_app as app from flask import render_template, session, Blueprint -from .controller import Eg026Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error +from ..examples.eg026_permissions_change_single_setting import Eg026PermissionsChangeSingleSettingController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE -eg = "eg026" -eg026 = Blueprint("eg026", __name__) +example_number = 26 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" +eg026 = Blueprint(eg, __name__) -@eg026.route("/eg026", methods=["POST"]) -@authenticate(eg=eg) +@eg026.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def permissions_change_single_setting(): """ 1. Get required arguments 2. Call the worker method 3. Render a response """ + example = get_example_by_number(session["manifest"], example_number, api) # 1. Get required arguments - args = Eg026Controller.get_args() + args = Eg026PermissionsChangeSingleSettingController.get_args() try: # 2. Call the worker method to change a setting in an existing permission profile - response, changed_settings = Eg026Controller.worker(args) + response, changed_settings = Eg026PermissionsChangeSingleSettingController.worker(args) permission_profile_id = response.permission_profile_id @@ -36,8 +41,7 @@ def permissions_change_single_setting(): # 3. Render the response return render_template( "example_done.html", - title="Changing setting in a permission profile", - h1="Changing setting in a permission profile", + title=example["ExampleName"], message=f"""Setting of permission profile has been changed!
""" f"""Permission profile ID: {permission_profile_id}.
Changed settings:""", changed_settings=changed_settings @@ -46,22 +50,25 @@ def permissions_change_single_setting(): except ApiException as err: return process_error(err) -@eg026.route("/eg026", methods=["GET"]) -@authenticate(eg=eg) +@eg026.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def get_view(): """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) args = { "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} "base_path": session["ds_base_path"], "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} } - permission_profiles = Eg026Controller.get_permissions_profiles(args) + permission_profiles = Eg026PermissionsChangeSingleSettingController.get_permissions_profiles(args) return render_template( - "eg026_permissions_change_single_setting.html", - title="Changing a setting in an existing permission profile", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", + "eSignature/eg026_permissions_change_single_setting.html", + title=example["ExampleName"], + example=example, + source_file= "eg026_permissions_change_single_setting.py", + source_url=DS_CONFIG["github_example_url"] + "eg026_permissions_change_single_setting.py", documentation=DS_CONFIG["documentation"] + eg, show_doc=DS_CONFIG["documentation"], permission_profiles=permission_profiles diff --git a/app/eSignature/views/eg027_permissions_delete.py b/app/eSignature/views/eg027_permissions_delete.py new file mode 100644 index 00000000..8d73f4b4 --- /dev/null +++ b/app/eSignature/views/eg027_permissions_delete.py @@ -0,0 +1,68 @@ +"""Example 027: Deleting a permission profile""" + +from os import path +from docusign_esign.client.api_exception import ApiException +from flask import current_app as app +from flask import render_template, session, request, Blueprint + +from ..examples.eg027_permissions_delete import Eg027PermissionsDeleteController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 27 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" +eg027 = Blueprint(eg, __name__) + +@eg027.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def permissions_delete(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + args = Eg027PermissionsDeleteController.get_args() + try: + # 2. Call the worker method to delete a permission profile + Eg027PermissionsDeleteController.worker(args) + app.logger.info(f"The permission profile has been deleted.") + + # 3. Render success response + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"The permission profile has been deleted!
" + ) + + except ApiException as err: + return process_error(err) + +@eg027.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + } + permission_profiles = Eg027PermissionsDeleteController.get_permissions_profiles(args) + return render_template( + "eSignature/eg027_permissions_delete.html", + title=example["ExampleName"], + example=example, + source_file= "eg027_permissions_delete.py", + source_url=DS_CONFIG["github_example_url"] + "eg027_permissions_delete.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + permission_profiles=permission_profiles + ) \ No newline at end of file diff --git a/app/eSignature/views/eg028_brand_creating.py b/app/eSignature/views/eg028_brand_creating.py new file mode 100644 index 00000000..57d2b5ad --- /dev/null +++ b/app/eSignature/views/eg028_brand_creating.py @@ -0,0 +1,65 @@ +"""Example 028: Creating a brand""" + +from os import path +from docusign_esign.client.api_exception import ApiException +from flask import current_app as app +from flask import render_template, session, request, Blueprint + +from ..examples.eg028_brand_creating import Eg028BrandCreatingController +from ...consts import languages +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 28 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference and url for this example +eg028 = Blueprint(eg, __name__) + +@eg028.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def brand_creating(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg028BrandCreatingController.get_args() + try: + # 2. Call the worker method to create a new brand + response = Eg028BrandCreatingController.worker(args) + brand_id = response.brands[0].brand_id + app.logger.info(f"Brand has been created. Brand ID: {brand_id}") + + # 3. Render the response + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"""The brand has been created and sent!
Brand ID: {brand_id}.""" + ) + + except ApiException as err: + return process_error(err) + +@eg028.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg028_brand_creating.html", + title=example["ExampleName"], + example=example, + source_file= "eg028_brand_creating.py", + source_url=DS_CONFIG["github_example_url"] + "eg028_brand_creating.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + languages=languages + ) \ No newline at end of file diff --git a/app/eSignature/examples/eg029_brands_apply_to_envelope/views.py b/app/eSignature/views/eg029_brands_apply_to_envelope.py similarity index 52% rename from app/eSignature/examples/eg029_brands_apply_to_envelope/views.py rename to app/eSignature/views/eg029_brands_apply_to_envelope.py index 0fb7245d..bd768d46 100644 --- a/app/eSignature/examples/eg029_brands_apply_to_envelope/views.py +++ b/app/eSignature/views/eg029_brands_apply_to_envelope.py @@ -4,36 +4,41 @@ from docusign_esign.client.api_exception import ApiException from flask import current_app as app from flask import render_template, session, Blueprint -from .controller import Eg029Controller -from ....docusign import authenticate -from ....ds_config import DS_CONFIG -from ....error_handlers import process_error -eg = "eg029" # Reference and URL for this example -eg029 = Blueprint("eg029", __name__) +from ..examples.eg029_brands_apply_to_envelope import Eg029BrandsApplyToEnvelopeController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE -@eg029.route("/eg029", methods=["POST"]) -@authenticate(eg=eg) +example_number = 29 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # Reference and URL for this example +eg029 = Blueprint(eg, __name__) + +@eg029.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def brands_apply_to_envelope(): """ 1. Get required arguments 2. Call the worker method 3. Render the response """ + example = get_example_by_number(session["manifest"], example_number, api) # 1. Get required arguments - args = Eg029Controller.get_args() + args = Eg029BrandsApplyToEnvelopeController.get_args() try: # 2. Call the worker method to apply brand to the envelope - response = Eg029Controller.worker(args) + response = Eg029BrandsApplyToEnvelopeController.worker(args) envelope_id = response.envelope_id app.logger.info(f"Brand has been applied to envelope. Envelope ID: {envelope_id}") # 3. Render the response return render_template( "example_done.html", - title="The brand applying to the envelope", - h1="The brand applying to the envelope", + title=example["ExampleName"], message=f"The brand has been applied to the envelope!
Envelope ID: {envelope_id}." ) @@ -41,10 +46,12 @@ def brands_apply_to_envelope(): return process_error(err) -@eg029.route("/eg029", methods=["GET"]) -@authenticate(eg=eg) +@eg029.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) def get_view(): """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) args = { "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} @@ -54,13 +61,14 @@ def get_view(): } # Get all brands to render in the template - brands = Eg029Controller.get_brands(args) + brands = Eg029BrandsApplyToEnvelopeController.get_brands(args) return render_template( - "eg029_brands_apply_to_envelope.html", - title="Applying a brand to an envelope", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py", + "eSignature/eg029_brands_apply_to_envelope.html", + title=example["ExampleName"], + example=example, + source_file= "eg029_brands_apply_to_envelope.py", + source_url=DS_CONFIG["github_example_url"] + "eg029_brands_apply_to_envelope.py", documentation=DS_CONFIG["documentation"] + eg, show_doc=DS_CONFIG["documentation"], brands=brands, diff --git a/app/eSignature/views/eg030_brands_apply_to_template.py b/app/eSignature/views/eg030_brands_apply_to_template.py new file mode 100644 index 00000000..061fd2ce --- /dev/null +++ b/app/eSignature/views/eg030_brands_apply_to_template.py @@ -0,0 +1,90 @@ +"""Example 030: Applying a brand and template to an envelope""" + +from os import path +from docusign_esign.client.api_exception import ApiException +from flask import current_app as app +from flask import render_template, session, Blueprint + +from ..examples.eg030_brands_apply_to_template import Eg030BrandsApplyToTemplateController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 30 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # Reference and URL for this example +eg030 = Blueprint(eg, __name__) + + +@eg030.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def brands_apply_to_template(): + """ + 1. Check the presence of a saved template_id + 2. Get required arguments + 3. Call the worker method + 4. Render a response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Check the presence of a saved template_id + if "template_id" in session: + # 2. Get required arguments + args = Eg030BrandsApplyToTemplateController.get_args() + try: + # 3: Call the worker method to apply the brand to the template + response = Eg030BrandsApplyToTemplateController.worker(args) + envelope_id = response.envelope_id + app.logger.info(f"The brand and template have been applied to the envelope. Envelope ID: {envelope_id}") + + # 4: Render the response + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"The brand and template have been applied to the envelope!
Envelope ID: {envelope_id}." + ) + + except ApiException as err: + return process_error(err) + + else: + return render_template( + "eSignature/eg030_brands_apply_to_template.html", + title=example["ExampleName"], + example=example, + template_ok=False, + source_file= "eg030_brands_apply_to_template.py", + source_url=DS_CONFIG["github_example_url"] + "eg030_brands_apply_to_template.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) + + +@eg030.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + } + brands = Eg030BrandsApplyToTemplateController.get_data(args) + return render_template( + "eSignature/eg030_brands_apply_to_template.html", + title=example["ExampleName"], + example=example, + template_ok="template_id" in session, + source_file= "eg030_brands_apply_to_template.py", + source_url=DS_CONFIG["github_example_url"] + "eg030_brands_apply_to_template.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + brands=brands, + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"], + ) \ No newline at end of file diff --git a/app/eSignature/views/eg031_bulk_send.py b/app/eSignature/views/eg031_bulk_send.py new file mode 100644 index 00000000..bee06ca5 --- /dev/null +++ b/app/eSignature/views/eg031_bulk_send.py @@ -0,0 +1,68 @@ +"""Example 031: Send an envelope to multiple recipients""" + +import json +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import current_app as app +from flask import render_template, Blueprint, session + +from ..examples.eg031_bulk_send import Eg031BulkSendController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 31 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference and url for this example +eg031 = Blueprint(eg, __name__) + +@eg031.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def bulk_send(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg031BulkSendController.get_args() + try: + # 2. Call the worker method for bulk sending + response = Eg031BulkSendController.worker(args) + queued = response.queued + app.logger.info(f"The envelope has been sent to multiple recipients.") + + # 3. Render the response + return render_template("example_done.html", + title=example["ExampleName"], + message=f"""Results from BulkSend:getBulkSendBatchStatus method:
""", + json=json.dumps(json.dumps(response.to_dict())) + ) + + except ApiException as err: + return process_error(err) + + +@eg031.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg031_bulk_send.html", + title=example["ExampleName"], + example=example, + source_file= "eg031_bulk_send.py", + source_url=DS_CONFIG["github_example_url"] + "eg031_bulk_send.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) \ No newline at end of file diff --git a/app/eSignature/views/eg032_pause_signature_workflow.py b/app/eSignature/views/eg032_pause_signature_workflow.py new file mode 100644 index 00000000..663b87cd --- /dev/null +++ b/app/eSignature/views/eg032_pause_signature_workflow.py @@ -0,0 +1,78 @@ +""" Example 032: Creating an envelope where the workflow is paused before the +envelope is sent to a second recipient """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint + +from ..examples.eg032_pause_signature_workflow import Eg032PauseSignatureWorkflowController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...docusign.utils import is_cfr +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 32 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # Reference (and URL) for this example +eg032 = Blueprint(eg, __name__) + + +@eg032.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def pause_signature_workflow(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response with envelopeId + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg032PauseSignatureWorkflowController.get_args() + try: + # 1. Call the worker method + results = Eg032PauseSignatureWorkflowController.worker(args) + except ApiException as err: + return process_error(err) + + session["paused_envelope_id"] = results["paused_envelope_id"] # Save for use by other examples which need an envelopeId + + # 2. Render success response with envelopeId + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"The envelope has been created and sent!" + f"
Envelope ID {results['paused_envelope_id']}.
" + f"

To resume a workflow after the first recipient signs " + f"the envelope use example 33.
" + ) + + +@eg032.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + cfr_status = is_cfr(session["ds_access_token"], session["ds_account_id"], session["ds_base_path"]) + if cfr_status == "enabled": + if DS_CONFIG["quickstart"] == "true": + return redirect(url_for("eg041.get_view")) + else: + return render_template("cfr_error.html", title="Error") + + return render_template( + "eSignature/eg032_pause_signature_workflow.html", + title=example["ExampleName"], + example=example, + source_file= "eg032_pause_signature_workflow.py", + source_url=DS_CONFIG["github_example_url"] + "eg032_pause_signature_workflow.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer1_name=DS_CONFIG["signer_name"], + signer1_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg033_unpause_signature_workflow.py b/app/eSignature/views/eg033_unpause_signature_workflow.py new file mode 100644 index 00000000..709835d6 --- /dev/null +++ b/app/eSignature/views/eg033_unpause_signature_workflow.py @@ -0,0 +1,74 @@ +""" Example 033: Resuming an envelope workflow that has been paused """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint + +from ..examples.eg033_unpause_signature_workflow import Eg033UnpauseSignatureWorkflowController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...docusign.utils import is_cfr +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 33 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # Reference (and URL) for this example +eg033 = Blueprint(eg, __name__) + + +@eg033.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def unpause_signature_workflow(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response with envelopeId + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg033UnpauseSignatureWorkflowController.get_args() + try: + # 1. Call the worker method + results = Eg033UnpauseSignatureWorkflowController.worker(args) + except ApiException as err: + return process_error(err) + + # 2. Render success response with envelopeId + return render_template( + "example_done.html", + envelope_ok=True, + title=example["ExampleName"], + message=f"The envelope workflow has been resumed and the envelope " + f"has been sent to a second recipient!
" + f"Envelope ID {results['envelope_id']}." + ) + + +@eg033.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + cfr_status = is_cfr(session["ds_access_token"], session["ds_account_id"], session["ds_base_path"]) + if cfr_status == "enabled": + if DS_CONFIG["quickstart"] == "true": + return redirect(url_for("eg041.get_view")) + else: + return render_template("cfr_error.html", title="Error") + + return render_template( + "eSignature/eg033_unpause_signature_workflow.html", + title=example["ExampleName"], + example=example, + envelope_ok="paused_envelope_id" in session, + source_file= "eg033_unpause_signature_workflow.py", + source_url=DS_CONFIG["github_example_url"] + "eg033_unpause_signature_workflow.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + ) diff --git a/app/eSignature/views/eg034_use_conditional_recipients.py b/app/eSignature/views/eg034_use_conditional_recipients.py new file mode 100644 index 00000000..b8b7d25b --- /dev/null +++ b/app/eSignature/views/eg034_use_conditional_recipients.py @@ -0,0 +1,75 @@ +""" Example 034: Creating an envelope where the workflow is routed +to different recipients based on the value of a transaction """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint + +from ..examples.eg034_use_conditional_recipients import Eg034UseConditionalRecipientsController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...docusign.utils import is_cfr +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 34 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # Reference (and URL) for this example +eg034 = Blueprint(eg, __name__) + + +@eg034.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def use_conditional_recipients(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response with envelopeId + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg034UseConditionalRecipientsController.get_args() + try: + # 1. Call the worker method + results = Eg034UseConditionalRecipientsController.worker(args) + except ApiException as err: + return process_error(err) + + # 2. Render success response with envelopeId + return render_template( + "example_done.html", + envelope_ok=True, + title=example["ExampleName"], + message=f"Envelope ID {results['envelope_id']} with the conditional" + f" routing criteria has been created and sent to the first recipient!" + ) + + +@eg034.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + cfr_status = is_cfr(session["ds_access_token"], session["ds_account_id"], session["ds_base_path"]) + if cfr_status == "enabled": + if DS_CONFIG["quickstart"] == "true": + return redirect(url_for("eg041.get_view")) + else: + return render_template("cfr_error.html", title="Error") + + return render_template( + "eSignature/eg034_use_conditional_recipients.html", + title=example["ExampleName"], + example=example, + source_file= "eg034_use_conditional_recipients.py", + source_url=DS_CONFIG["github_example_url"] + "eg034_use_conditional_recipients.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer1_name=DS_CONFIG["signer_name"], + signer1_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg035_scheduled_sending.py b/app/eSignature/views/eg035_scheduled_sending.py new file mode 100644 index 00000000..c0ef3259 --- /dev/null +++ b/app/eSignature/views/eg035_scheduled_sending.py @@ -0,0 +1,87 @@ + +""" Example 035: Scheduled sending and delayed routing """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint, request + +from ..examples.eg035_scheduled_sending import Eg035ScheduledSendingController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import pattern, API_TYPE + +example_number = 35 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg035 = Blueprint(eg, __name__) + +def get_args(): + """Get request and session arguments""" + + # More data validation would be a good idea here + # Strip anything other than characters listed + signer_email = pattern.sub("", request.form.get("signer_email")) + signer_name = pattern.sub("", request.form.get("signer_name")) + resume_date = request.form.get("resume_date") + envelope_args = { + "signer_email": signer_email, + "signer_name": signer_name, + "resume_date": resume_date, + "status": "sent", + } + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_args": envelope_args + } + return args + +@eg035.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def sign_by_email(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response with envelopeId + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = get_args() + try: + # 1. Call the worker method + results = Eg035ScheduledSendingController.worker(args) + print(results) + except ApiException as err: + return process_error(err) + + # 2. Render success response with envelopeId + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"The envelope has been created and scheduled!
Envelope ID: {results['envelope_id']}." + ) + + +@eg035.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg035_scheduled_sending.html", + title=example["ExampleName"], + example=example, + source_file="eg035_scheduled_sending.py", + source_url=DS_CONFIG["github_example_url"] + "eg035_scheduled_sending.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg036_delayed_routing.py b/app/eSignature/views/eg036_delayed_routing.py new file mode 100644 index 00000000..ace7b655 --- /dev/null +++ b/app/eSignature/views/eg036_delayed_routing.py @@ -0,0 +1,97 @@ + +""" Example 036: Delayed routing """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint, request + +from ..examples.eg036_delayed_routing import Eg036DelayedRoutingController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...docusign.utils import is_cfr +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import pattern, API_TYPE + +example_number = 36 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg036 = Blueprint(eg, __name__) + +def get_args(): + """Get request and session arguments""" + + signer_email = pattern.sub("", request.form.get("signer_email")) + signer_name = pattern.sub("", request.form.get("signer_name")) + signer_email2 = pattern.sub("", request.form.get("signer_email2")) + signer_name2 = pattern.sub("", request.form.get("signer_name2")) + delay = request.form.get("delay") + envelope_args = { + "signer_email": signer_email, + "signer_name": signer_name, + "signer_email2": signer_email2, + "signer_name2": signer_name2, + "delay": delay, + "status": "sent", + } + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_args": envelope_args + } + return args + +@eg036.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def sign_by_email(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response with envelopeId + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = get_args() + try: + # 2. Call the worker method + results = Eg036DelayedRoutingController.worker(args) + print(results) + except ApiException as err: + return process_error(err) + + # 3. Render success response with envelopeId + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"The envelope has been created and sent!
Envelope ID: {results['envelope_id']}." + ) + + +@eg036.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + cfr_status = is_cfr(session["ds_access_token"], session["ds_account_id"], session["ds_base_path"]) + if cfr_status == "enabled": + if DS_CONFIG["quickstart"] == "true": + return redirect(url_for("eg041.get_view")) + else: + return render_template("cfr_error.html", title="Error") + + return render_template( + "eSignature/eg036_delayed_routing.html", + title=example["ExampleName"], + example=example, + source_file="eg036_delayed_routing.py", + source_url=DS_CONFIG["github_example_url"] + "eg036_delayed_routing.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg037_sms_delivery.py b/app/eSignature/views/eg037_sms_delivery.py new file mode 100644 index 00000000..decde4fe --- /dev/null +++ b/app/eSignature/views/eg037_sms_delivery.py @@ -0,0 +1,73 @@ +""" Example 037: SMS Delivery """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint + +from ..examples.eg037_sms_delivery import Eg037SMSDeliveryController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...docusign.utils import is_cfr +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 37 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg037 = Blueprint(eg, __name__) + + +@eg037.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def sign_by_email(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response with envelopeId + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg037SMSDeliveryController.get_args() + try: + # 1. Call the worker method + results = Eg037SMSDeliveryController.worker(args) + except ApiException as err: + return process_error(err) + + session["envelope_id"] = results["envelope_id"] # Save for use by other examples which need an envelopeId + + # 2. Render success response with envelopeId + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"The envelope has been created and sent!
Envelope ID {results['envelope_id']}." + ) + + +@eg037.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + cfr_status = is_cfr(session["ds_access_token"], session["ds_account_id"], session["ds_base_path"]) + if cfr_status == "enabled": + if DS_CONFIG["quickstart"] == "true": + return redirect(url_for("eg041.get_view")) + else: + return render_template("cfr_error.html", title="Error") + + return render_template( + "eSignature/eg037_sms_delivery.html", + title=example["ExampleName"], + example=example, + source_file= "eg037_sms_delivery.py", + source_url=DS_CONFIG["github_example_url"] + "eg037_sms_delivery.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"] + ) diff --git a/app/eSignature/views/eg038_responsive_signing.py b/app/eSignature/views/eg038_responsive_signing.py new file mode 100644 index 00000000..bda4065b --- /dev/null +++ b/app/eSignature/views/eg038_responsive_signing.py @@ -0,0 +1,61 @@ +"""Example 038: Responsive signing""" + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, redirect, Blueprint, session + +from ..examples.eg038_responsive_signing import Eg038ResponsiveSigning +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 38 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg038 = Blueprint(eg, __name__) + + +@eg038.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def embedded_signing(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Redirect the user to the embedded signing + """ + try: + # 1. Get required arguments + args = Eg038ResponsiveSigning.get_args() + # 2. Call the worker method + results = Eg038ResponsiveSigning.worker(args) + except ApiException as err: + return process_error(err) + + # 3. Redirect the user to the embedded signing + # Don"t use an iFrame! + # State can be stored/recovered using the framework"s session or a + # query parameter on the returnUrl (see the makeRecipientViewRequest method) + return redirect(results["redirect_url"]) + + +@eg038.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg038_responsive_signing.html", + title=example["ExampleName"], + example=example, + source_file= "eg038_responsive_signing.py", + source_url=DS_CONFIG["github_example_url"] + "eg038_responsive_signing.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg039_in_person_signer.py b/app/eSignature/views/eg039_in_person_signer.py new file mode 100644 index 00000000..b590a4e5 --- /dev/null +++ b/app/eSignature/views/eg039_in_person_signer.py @@ -0,0 +1,67 @@ +""" Example 039: In Person Signer """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, redirect, session, Blueprint, request + +from ..examples.eg039_in_person_signer import Eg039InPersonSigner +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...docusign.utils import is_cfr +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 39 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg039 = Blueprint(eg, __name__) + +@eg039.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def in_person_signer(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response with envelopeId + """ + + # 1. Get required arguments + args = Eg039InPersonSigner.get_args() + try: + # 1. Call the worker method + results = Eg039InPersonSigner.worker(args) + except ApiException as err: + return process_error(err) + + session["envelope_id"] = results["envelope_id"] # Save for use by other examples which need an envelopeId + + # 2. Redirect to in person signing session + return redirect(results["redirect_url"]) + + +@eg039.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + cfr_status = is_cfr(session["ds_access_token"], session["ds_account_id"], session["ds_base_path"]) + if cfr_status == "enabled": + if DS_CONFIG["quickstart"] == "true": + return redirect(url_for("eg041.get_view")) + else: + return render_template("cfr_error.html", title="Error") + + return render_template( + "eSignature/eg039_in_person_signer.html", + title=example["ExampleName"], + example=example, + source_file="eg039_in_person_signer.py", + source_url=DS_CONFIG["github_example_url"] + "eg039_in_person_signer.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + ) diff --git a/app/eSignature/views/eg040_document_visibility.py b/app/eSignature/views/eg040_document_visibility.py new file mode 100644 index 00000000..6ea65f23 --- /dev/null +++ b/app/eSignature/views/eg040_document_visibility.py @@ -0,0 +1,71 @@ +"""Example 040: Document Visibility""" + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, redirect, Blueprint, session + +from ..examples.eg040_document_visibility import Eg040DocumentVisibility +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...docusign.utils import is_cfr +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 40 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg040 = Blueprint(eg, __name__) + + +@eg040.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def embedded_signing(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Redirect the user to the embedded signing + """ + example = get_example_by_number(session["manifest"], example_number, api) + + try: + # 1. Get required arguments + args = Eg040DocumentVisibility.get_args() + # 2. Call the worker method + results = Eg040DocumentVisibility.worker(args, DS_CONFIG["doc_docx"], DS_CONFIG["doc_pdf"]) + except ApiException as err: + return process_error(err) + + # 3. Render success response with envelopeId + return render_template( + "example_done.html", + title=example["ExampleName"], + message=f"The envelope has been created and sent!
Envelope ID {results['envelope_id']}." + ) + +@eg040.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + cfr_status = is_cfr(session["ds_access_token"], session["ds_account_id"], session["ds_base_path"]) + if cfr_status == "enabled": + if DS_CONFIG["quickstart"] == "true": + return redirect(url_for("eg041.get_view")) + else: + return render_template("cfr_error.html", title="Error") + + return render_template( + "eSignature/eg040_document_visibility.html", + title=example["ExampleName"], + example=example, + source_file= "eg040_document_visibility.py", + source_url=DS_CONFIG["github_example_url"] + "eg040_document_visibility.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer1_name=DS_CONFIG["signer_name"], + signer1_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg041_cfr_embedded_signing.py b/app/eSignature/views/eg041_cfr_embedded_signing.py new file mode 100644 index 00000000..36f4034b --- /dev/null +++ b/app/eSignature/views/eg041_cfr_embedded_signing.py @@ -0,0 +1,68 @@ +"""Example 041: Use embedded signing with CFR Part 11""" + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, redirect, Blueprint, current_app as app, session + +from ..examples.eg041_cfr_embedded_signing import Eg041CFREmbeddedSigningController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 41 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg041 = Blueprint(eg, __name__) + + +@eg041.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def embedded_signing(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Redirect the user to the embedded signing + """ + try: + # 1. Get required arguments + args = Eg041CFREmbeddedSigningController.get_args() + # 2. Call the worker method + results = Eg041CFREmbeddedSigningController.worker(args) + except ApiException as err: + return process_error(err) + + # 3. Redirect the user to the embedded signing + # Don"t use an iFrame! + # State can be stored/recovered using the framework"s session or a + # query parameter on the returnUrl (see the makeRecipientViewRequest method) + return redirect(results["redirect_url"]) + + +@eg041.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + args = { + "account_id": session["ds_account_id"], # represent your {ACCOUNT_ID} + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], # represent your {ACCESS_TOKEN} + } + + workflow_id = Eg041CFREmbeddedSigningController.get_workflow(args) + + return render_template( + "eSignature/eg041_cfr_embedded_signing.html", + title=example["ExampleName"], + example=example, + source_file="eg041_cfr_embedded_signing.py", + source_url=DS_CONFIG["github_example_url"] + "eg041_cfr_embedded_signing.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"], + workflow_id=workflow_id + ) diff --git a/app/eSignature/views/eg042_document_generation.py b/app/eSignature/views/eg042_document_generation.py new file mode 100644 index 00000000..d8345084 --- /dev/null +++ b/app/eSignature/views/eg042_document_generation.py @@ -0,0 +1,64 @@ +""" Example 002: Remote signer, cc, envelope has three documents """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint, request + +from ..examples.eg042_document_generation import Eg042DocumentGenerationController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import pattern, API_TYPE + +example_number = 42 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg042 = Blueprint(eg, __name__) + + +@eg042.route(f"/{eg}", methods=["POST"]) +@authenticate(eg=eg, api=api) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +def sign_by_email(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response with envelopeId + """ + + # 1. Get required arguments + args = Eg042DocumentGenerationController.get_args() + try: + # 1. Call the worker method + results = Eg042DocumentGenerationController.worker(args) + except ApiException as err: + return process_error(err) + + # 2. Render success response with envelopeId + example = get_example_by_number(session["manifest"], example_number, api) + return render_template( + "example_done.html", + title=example["ExampleName"], + message=example["ResultsPageText"].format(results.envelope_id) + ) + + +@eg042.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg042_document_generation.html", + title=example["ExampleName"], + example=example, + source_file="eg042_document_generation.py", + source_url=DS_CONFIG["github_example_url"] + "eg042_document_generation.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg043_shared_access.py b/app/eSignature/views/eg043_shared_access.py new file mode 100644 index 00000000..dbb74d78 --- /dev/null +++ b/app/eSignature/views/eg043_shared_access.py @@ -0,0 +1,159 @@ +""" Example 043: Share access to a DocuSign envelope inbox """ + +import json + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint, redirect, current_app, url_for, request + +from ..examples.eg043_shared_access import Eg043SharedAccessController +from ...docusign import authenticate, ensure_manifest, get_example_by_number, get_user_info +from ...docusign.utils import ds_logout_internal, authenticate_agent +from ...ds_config import DS_CONFIG, DS_JWT +from ...error_handlers import process_error +from ...consts import pattern, API_TYPE + +example_number = 43 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg043 = Blueprint(eg, __name__) + + +@eg043.route(f"/{eg}", methods=["POST"]) +@authenticate(eg=eg, api=api) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +def create_agent(): + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "email": request.form.get("email"), + "user_name": pattern.sub("", request.form.get("user_name")), + "activation": pattern.sub("", request.form.get("activation")) + } + try: + # 1. Create the agent user + results = Eg043SharedAccessController.create_agent(args) + except ApiException as err: + return process_error(err) + + session["agent_user_id"] = results.user_id + + example = get_example_by_number(session["manifest"], example_number, api) + return render_template( + "example_done.html", + title="Agent user created", + message=example["ResultsPageText"], + json=json.dumps(json.dumps(results.to_dict())), + redirect_url=f"/{eg}auth" + ) + + +@eg043.route(f"/{eg}auth", methods=["GET"]) +@authenticate(eg=eg, api=api) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +def create_authorization(): + user_info = get_user_info( + session["ds_access_token"], + session["ds_base_path"], + DS_CONFIG["authorization_server"].replace("https://", "") + ) + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "user_id": user_info.sub, + "agent_user_id": session["agent_user_id"] + } + try: + # 2. Create the authorization for agent user + Eg043SharedAccessController.create_authorization(args) + except ApiException as err: + error_body_json = err and hasattr(err, "body") and err.body + error_body = json.loads(error_body_json) + error_code = error_body and "errorCode" in error_body and error_body["errorCode"] + if error_code == "USER_NOT_FOUND": + example = get_example_by_number(session["manifest"], example_number, api) + additional_page_data = next((p for p in example["AdditionalPage"] if p["Name"] == "user_not_found"), + None) + return render_template( + "example_done.html", + title="Agent user created", + message=additional_page_data["ResultsPageText"], + redirect_url=f"/{eg}auth" + ) + + return process_error(err) + + session["principal_user_id"] = user_info.sub + + example = get_example_by_number(session["manifest"], example_number, api) + additional_page_data = next((p for p in example["AdditionalPage"] if p["Name"] == "authenticate_as_agent"), None) + return render_template( + "example_done.html", + title="Authenticate as the agent", + message=additional_page_data["ResultsPageText"], + redirect_url=f"/{eg}reauthenticate" + ) + + +@eg043.route(f"/{eg}reauthenticate", methods=["GET"]) +def reauthenticate(): + # 3. Logout principal user and redirect to page with the list of envelopes, login as agent user + ds_logout_internal() + current_app.config["isLoggedIn"] = False + return redirect(url_for(f"{eg}.list_envelopes")) + + +@eg043.route(f"/{eg}envelopes", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate_agent(eg=eg) +def list_envelopes(): + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "user_id": session["principal_user_id"] + } + try: + # 4. Retrieve the list of envelopes + results = Eg043SharedAccessController.get_envelopes(args) + except ApiException as err: + return process_error(err) + + example = get_example_by_number(session["manifest"], example_number, api) + + if int(results.result_set_size) > 0: + additional_page = next((p for p in example["AdditionalPage"] if p["Name"] == "list_status_successful"), None) + return render_template( + "example_done.html", + title="Principal's envelopes visible in the agent's Shared Access UI", + message=additional_page["ResultsPageText"], + json=json.dumps(json.dumps(results.to_dict())) + ) + + additional_page = next((p for p in example["AdditionalPage"] if p["Name"] == "list_status_unsuccessful"), None) + return render_template( + "example_done.html", + title="No envelopes in the principal user's account", + message=additional_page["ResultsPageText"] + ) + + +@eg043.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg043_shared_access.html", + title=example["ExampleName"], + example=example, + source_file="eg043_shared_access.py", + source_url=DS_CONFIG["github_example_url"] + "eg043_shared_access.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg044_focused_view.py b/app/eSignature/views/eg044_focused_view.py new file mode 100644 index 00000000..dbc747a1 --- /dev/null +++ b/app/eSignature/views/eg044_focused_view.py @@ -0,0 +1,63 @@ +"""Example 044: Focused view""" + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, Blueprint, session + +from ..examples.eg044_focused_view import Eg044FocusedViewController +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import API_TYPE + +example_number = 44 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +eg044 = Blueprint(eg, __name__) + + +@eg044.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def embedded_signing(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Redirect the user to the embedded signing + """ + try: + # 1. Get required arguments + args = Eg044FocusedViewController.get_args() + # 2. Call the worker method + results = Eg044FocusedViewController.worker(args) + except ApiException as err: + return process_error(err) + + session["envelope_id"] = results["envelope_id"] + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg044_embed.html", + title=example["ExampleName"], + url=results["redirect_url"], + integration_key=DS_CONFIG["ds_client_id"] + ) + + +@eg044.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg044_focused_view.html", + title=example["ExampleName"], + example=example, + source_file="eg044_focused_view.py", + source_url=DS_CONFIG["github_example_url"] + "eg044_focused_view.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eSignature/views/eg045_delete_restore_envelope.py b/app/eSignature/views/eg045_delete_restore_envelope.py new file mode 100644 index 00000000..34289069 --- /dev/null +++ b/app/eSignature/views/eg045_delete_restore_envelope.py @@ -0,0 +1,155 @@ +""" Example 045: Delete and undelete an Envelope """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint, request, redirect + +from ..examples.eg045_delete_restore_envelope import Eg045DeleteRestoreEnvelopeController +from ..utils import get_folder_id_by_name +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import pattern, API_TYPE + +example_number = 45 +api = API_TYPE["ESIGNATURE"] +eg = f"eg0{example_number}" # reference (and url) for this example +restore_endpoint = f"{eg}restore" +delete_folder_id = "recyclebin" +restore_folder_id = "sentitems" +eg045 = Blueprint(eg, __name__) + +@eg045.route(f"/{eg}", methods=["POST"]) +@authenticate(eg=eg, api=api) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +def delete_envelope(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response + """ + + # 1. Get required arguments + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_id": pattern.sub("", request.form.get("envelope_id")), + "delete_folder_id": delete_folder_id + } + try: + # 2. Call the worker method + Eg045DeleteRestoreEnvelopeController.delete_envelope(args) + except ApiException as err: + return process_error(err) + + session["envelope_id"] = args["envelope_id"] # Save for use by second part of example + + # 3. Render success response + example = get_example_by_number(session["manifest"], example_number, api) + additional_page_data = next( + (p for p in example["AdditionalPage"] if p["Name"] == "envelope_is_deleted"), + None + ) + return render_template( + "example_done.html", + title=example["ExampleName"], + message=additional_page_data["ResultsPageText"].format(args["envelope_id"]), + redirect_url=restore_endpoint + ) + +@eg045.route(f"/{restore_endpoint}", methods=["POST"]) +@authenticate(eg=eg, api=api) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +def restore_envelope(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response + """ + + # 1. Get required arguments + folder_name = pattern.sub("", request.form.get("folder_name")) + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_id": pattern.sub("", session.get("envelope_id")), + "from_folder_id": delete_folder_id + } + + example = get_example_by_number(session["manifest"], example_number, api) + try: + # 2. Call the worker method + folders = Eg045DeleteRestoreEnvelopeController.get_folders(args) + args["folder_id"] = get_folder_id_by_name(folders.folders, folder_name) + + if args["folder_id"] is None: + additional_page_data = next( + (p for p in example["AdditionalPage"] if p["Name"] == "folder_does_not_exist"), + None + ) + + return render_template( + "example_done.html", + title=example["ExampleName"], + message=additional_page_data["ResultsPageText"].format(folder_name), + redirect_url=restore_endpoint + ) + + Eg045DeleteRestoreEnvelopeController.move_envelope_to_folder(args) + except ApiException as err: + return process_error(err) + + # 3. Render success response with envelopeId + return render_template( + "example_done.html", + title=example["ExampleName"], + message=example["ResultsPageText"].format(session.get("envelope_id", ""), args["folder_id"], folder_name) + ) + +@eg045.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "eSignature/eg045_delete_envelope.html", + title=example["ExampleName"], + example=example, + envelope_id=session.get("envelope_id", ""), + submit_button_text=session["manifest"]["SupportingTexts"]["HelpingTexts"]["SubmitButtonDeleteText"], + source_file="eg045_delete_restore_envelope.py", + source_url=DS_CONFIG["github_example_url"] + "eg045_delete_restore_envelope.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) + +@eg045.route(f"/{restore_endpoint}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_restore_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + if not session.get("envelope_id"): + return redirect(eg) + + return render_template( + "eSignature/eg045_restore_envelope.html", + title=example["ExampleName"], + example=example, + envelope_id=session.get("envelope_id"), + submit_button_text=session["manifest"]["SupportingTexts"]["HelpingTexts"]["SubmitButtonRestoreText"], + source_file="eg045_delete_restore_envelope.py", + source_url=DS_CONFIG["github_example_url"] + "eg045_delete_restore_envelope.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eg001_embedded_signing.py b/app/eg001_embedded_signing.py new file mode 100644 index 00000000..5195a109 --- /dev/null +++ b/app/eg001_embedded_signing.py @@ -0,0 +1,69 @@ +"""Example 001: Use embedded signing""" + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, redirect, Blueprint, current_app as app, session, url_for + +from .eSignature.examples.eg001_embedded_signing import Eg001EmbeddedSigningController +from .eSignature.examples.eg041_cfr_embedded_signing import Eg041CFREmbeddedSigningController +from .docusign import authenticate, ensure_manifest, get_example_by_number +from .ds_config import DS_CONFIG +from .error_handlers import process_error +from .consts import API_TYPE +from .docusign.utils import is_cfr + +example_number = 1 +api = API_TYPE["ESIGNATURE"] +eg = f"eg00{example_number}" # reference (and url) for this example +eg001 = Blueprint(eg, __name__) + + +@eg001.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def embedded_signing(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Redirect the user to the embedded signing + """ + try: + # 1. Get required arguments + args = Eg001EmbeddedSigningController.get_args() + # 2. Call the worker method + results = Eg001EmbeddedSigningController.worker(args) + except ApiException as err: + return process_error(err) + + # 3. Redirect the user to the embedded signing + # Don"t use an iFrame! + # State can be stored/recovered using the framework"s session or a + # query parameter on the returnUrl (see the makeRecipientViewRequest method) + return redirect(results["redirect_url"]) + + +@eg001.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + # Check if account is a CFR Part 11 account and redirect accordingly for Quickstart + cfr_status = is_cfr(session["ds_access_token"], session["ds_account_id"], session["ds_base_path"]) + if cfr_status == "enabled": + if DS_CONFIG["quickstart"] == "true": + return redirect(url_for("eg041.get_view")) + else: + return render_template("cfr_error.html", title="Error") + + return render_template( + "eg001_embedded_signing.html", + title=example["ExampleName"], + example=example, + source_file="eg001_embedded_signing.py", + source_url=DS_CONFIG["github_example_url"] + "eg001_embedded_signing.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/eg001_embedded_signing/__init__.py b/app/eg001_embedded_signing/__init__.py deleted file mode 100644 index 11c5cf04..00000000 --- a/app/eg001_embedded_signing/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg001 diff --git a/app/eg001_embedded_signing/views.py b/app/eg001_embedded_signing/views.py deleted file mode 100644 index 9e3f9ad0..00000000 --- a/app/eg001_embedded_signing/views.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Example 001: Use embedded signing""" - -from os import path - -from docusign_esign.client.api_exception import ApiException -from flask import render_template, redirect, Blueprint - -from .controller import Eg001Controller -from ..docusign import authenticate -from ..ds_config import DS_CONFIG -from ..error_handlers import process_error - -eg = "eg001" # reference (and url) for this example -eg001 = Blueprint("eg001", __name__) - - -@eg001.route("/eg001", methods=["POST"]) -@authenticate(eg=eg) -def embedded_signing(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Redirect the user to the embedded signing - """ - try: - # 1. Get required arguments - args = Eg001Controller.get_args() - # 2. Call the worker method - results = Eg001Controller.worker(args) - except ApiException as err: - return process_error(err) - - # 3. Redirect the user to the embedded signing - # Don"t use an iFrame! - # State can be stored/recovered using the framework"s session or a - # query parameter on the returnUrl (see the makeRecipientViewRequest method) - return redirect(results["redirect_url"]) - - -@eg001.route("/eg001", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """responds with the form for the example""" - return render_template( - "eg001_embedded_signing.html", - title="Use embedded signing", - source_file=path.basename(path.dirname(__file__)) + "/controller.py", - source_url="https://github.com/docusign/code-examples-python/tree/master/app/" + path.basename(path.dirname(__file__)) + "/controller.py", - documentation=DS_CONFIG["documentation"] + eg, - show_doc=DS_CONFIG["documentation"], - signer_name=DS_CONFIG["signer_name"], - signer_email=DS_CONFIG["signer_email"] - ) diff --git a/app/error_handlers.py b/app/error_handlers.py index 683b154a..21e4e291 100644 --- a/app/error_handlers.py +++ b/app/error_handlers.py @@ -1,6 +1,7 @@ import json from flask import render_template +from .api_type import EXAMPLES_API_TYPE def process_error(err): @@ -15,7 +16,11 @@ def process_error(err): # Handle error specific for use conditional recipients example (eg34) if error_code == "WORKFLOW_UPDATE_RECIPIENTROUTING_NOT_ALLOWED": - return render_template("error_eg34.html") + return render_template("eSignature/error_eg34.html") + + if EXAMPLES_API_TYPE["Monitor"]: + if ("(403" in str(err)): + return render_template("error_enable_monitor.html") # In production, may want to provide customized error messages and # remediation advice to the user. diff --git a/app/jwt_config_sample.py b/app/jwt_config_sample.py new file mode 100644 index 00000000..fdffb2c2 --- /dev/null +++ b/app/jwt_config_sample.py @@ -0,0 +1,8 @@ +DS_JWT = { + "ds_client_id": "{INTEGRATION_KEY_JWT}", + "ds_impersonated_user_id": "{IMPERSONATED_USER_ID}", # The id of the user. + "private_key_file": "./app/private.key", # Create a new file in your repo source folder named private.key then copy and paste your RSA private key there and save it. + "authorization_server": "account-d.docusign.com", + "doc_docx": "World_Wide_Corp_Battle_Plan_Trafalgar.docx", + "doc_pdf": "World_Wide_Corp_lorem.pdf" +} \ No newline at end of file diff --git a/app/jwt_helpers/__init__.py b/app/jwt_helpers/__init__.py new file mode 100644 index 00000000..7d84ba6e --- /dev/null +++ b/app/jwt_helpers/__init__.py @@ -0,0 +1 @@ +from .jwt_helper import create_api_client, get_jwt_token, get_private_key \ No newline at end of file diff --git a/app/jwt_helpers/jwt_helper.py b/app/jwt_helpers/jwt_helper.py new file mode 100644 index 00000000..c61f20b6 --- /dev/null +++ b/app/jwt_helpers/jwt_helper.py @@ -0,0 +1,39 @@ +from docusign_esign import ApiClient +from os import path + +def get_jwt_token(private_key, scopes, auth_server, client_id, impersonated_user_id): + """Get the jwt token""" + api_client = ApiClient() + api_client.set_base_path(auth_server) + response = api_client.request_jwt_user_token( + client_id=client_id, + user_id=impersonated_user_id, + oauth_host_name=auth_server, + private_key_bytes=private_key, + expires_in=4000, + scopes=scopes + ) + return response + +def get_private_key(private_key_path): + """ + Check that the private key present in the file and if it is, get it from the file. + In the opposite way get it from config variable. + """ + private_key_file = path.abspath(private_key_path) + + if path.isfile(private_key_file): + with open(private_key_file) as private_key_file: + private_key = private_key_file.read() + else: + private_key = private_key_path + + return private_key + +def create_api_client(base_path, access_token): + """Create api client and construct API headers""" + api_client = ApiClient() + api_client.host = base_path + api_client.set_default_header(header_name="Authorization", header_value=f"Bearer {access_token}") + + return api_client \ No newline at end of file diff --git a/app/monitor/__init__.py b/app/monitor/__init__.py index 82851216..1e698b5f 100644 --- a/app/monitor/__init__.py +++ b/app/monitor/__init__.py @@ -1 +1 @@ -from .examples import eg001 +from .views import meg001 diff --git a/app/monitor/examples/__init__.py b/app/monitor/examples/__init__.py deleted file mode 100644 index 6c8a24c1..00000000 --- a/app/monitor/examples/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .eg001_get_monitoring_data import eg001 diff --git a/app/monitor/examples/eg001_get_monitoring_data.py b/app/monitor/examples/eg001_get_monitoring_data.py new file mode 100644 index 00000000..9b9c30e5 --- /dev/null +++ b/app/monitor/examples/eg001_get_monitoring_data.py @@ -0,0 +1,53 @@ +from docusign_monitor import DataSetApi +from flask import session +from datetime import datetime, timedelta, timezone + +from app.monitor.utils import create_monitor_api_client + +class Eg001GetMonitoringDataController: + @staticmethod + def get_args(): + """Get required session and request arguments""" + return { + "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} + "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} + } + + @staticmethod + def worker(args): + """ + 1. Create an API client with headers + 2. Get your monitor data via SDK + """ + # Create an API client with headers + #ds-snippet-start:Monitor1Step2 + api_client = create_monitor_api_client( + access_token=args["access_token"] + ) + #ds-snippet-end:Monitor1Step2 + #ds-snippet-start:Monitor1Step3 + cursor_date = datetime.now(timezone.utc).replace(year=datetime.now(timezone.utc).year - 1) + dataset_api = DataSetApi(api_client=api_client) + + cursor_value = cursor_date.strftime('%Y-%m-%dT00:00:00Z') + limit = 2000 + function_results = [] + complete = False + + while not complete: + cursored_results = dataset_api.get_stream( + data_set_name="monitor", + version="2.0", + limit=limit, + cursor=cursor_value + ) + end_cursor = cursored_results.end_cursor + + if end_cursor == cursor_value: + complete = True + else: + cursor_value = end_cursor + function_results.append(cursored_results.data) + + #ds-snippet-end:Monitor1Step3 + return function_results diff --git a/app/monitor/examples/eg001_get_monitoring_data/__init__.py b/app/monitor/examples/eg001_get_monitoring_data/__init__.py deleted file mode 100644 index 11c5cf04..00000000 --- a/app/monitor/examples/eg001_get_monitoring_data/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import eg001 diff --git a/app/monitor/examples/eg001_get_monitoring_data/controller.py b/app/monitor/examples/eg001_get_monitoring_data/controller.py deleted file mode 100644 index 9167bb24..00000000 --- a/app/monitor/examples/eg001_get_monitoring_data/controller.py +++ /dev/null @@ -1,56 +0,0 @@ -from docusign_monitor import DataSetApi -from flask import session, json - -from app.monitor.utils import create_monitor_api_client - - -class Eg001Controller: - @staticmethod - def get_args(): - """Get required session and request arguments""" - return { - "account_id": session["ds_account_id"], # Represents your {ACCOUNT_ID} - "access_token": session["ds_access_token"], # Represents your {ACCESS_TOKEN} - } - - @staticmethod - def worker(args): - """ - 1. Create an API client with headers - 2. Get your monitor data via SDK - """ - - # Step 2 start - - # Create an API client with headers - api_client = create_monitor_api_client( - access_token=args["access_token"] - ) - - # Step 2 end - - #Step 3 start - - dataset_api = DataSetApi(api_client=api_client) - - cursor = "" - result = [] - while True: - response = dataset_api.get_stream_for_dataset( - data_set_name="monitor", - version="2.0", - cursor=cursor - ) - - # If the endCursor from the response is the same as the one - # that you already have, - # it means that you have reached the end of the records - if response["endCursor"] == cursor: - break - - result.extend(response["data"]) - cursor = response["endCursor"] - - # Step 3 end - - return result diff --git a/app/monitor/examples/eg001_get_monitoring_data/views.py b/app/monitor/examples/eg001_get_monitoring_data/views.py deleted file mode 100644 index 62ae8d90..00000000 --- a/app/monitor/examples/eg001_get_monitoring_data/views.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Example 001: Get monitoring data. """ - -import json - -from docusign_monitor.client.api_exception import ApiException -from flask import Blueprint, render_template, current_app - -from app.docusign import authenticate -from app.error_handlers import process_error -from .controller import Eg001Controller - -eg = "eg001" # Reference (and URL) for this example -eg001 = Blueprint(eg, __name__) - - -@eg001.route("/eg001", methods=["GET"]) -@authenticate(eg=eg) -def get_view(): - """ - 1. Get required arguments - 2. Call the worker method - 3. Render the response - """ - - # 1. Get required arguments - args = Eg001Controller.get_args() - try: - # 2. Call the worker method to get your monitor data - results = Eg001Controller.worker(args) - current_app.logger.info(f"""Got your monitor data""") - except ApiException as err: - return process_error(err) - - # 3. Render the response - return render_template( - "example_done.html", - title="Get monitoring data", - h1="Get monitoring data", - json=json.dumps(json.dumps(results, default=str)) - ) diff --git a/app/monitor/templates/404.html b/app/monitor/templates/404.html deleted file mode 100644 index 307da311..00000000 --- a/app/monitor/templates/404.html +++ /dev/null @@ -1,7 +0,0 @@ - -{% extends "base.html" %} - -{% block content %} -

File Not Found

-

Continue

-{% endblock %} diff --git a/app/monitor/templates/500.html b/app/monitor/templates/500.html deleted file mode 100644 index 7caf7e85..00000000 --- a/app/monitor/templates/500.html +++ /dev/null @@ -1,7 +0,0 @@ - -{% extends "base.html" %} - -{% block content %} -

{{ 'An unexpected error has occurred' }}

-

{{ 'Back' }}

-{% endblock %} diff --git a/app/monitor/templates/base.html b/app/monitor/templates/base.html deleted file mode 100644 index 878232d8..00000000 --- a/app/monitor/templates/base.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - {{ title }} - - - - - - - - - - - - - - - - - -
-
- {% block content %}{% endblock %} -
-
- - - - - - - - - - - - - - - - {% set json_messages = get_flashed_messages() | tojson %} - - - \ No newline at end of file diff --git a/app/monitor/templates/example_done.html b/app/monitor/templates/example_done.html deleted file mode 100644 index 191b95e2..00000000 --- a/app/monitor/templates/example_done.html +++ /dev/null @@ -1,27 +0,0 @@ - {% extends "base.html" %} {% block content %} - -

{{ h1 }}

-

{{ message | safe }}

-{% if link %} -
- {{ link_text }} -
-{% endif %} - -{% if json %} - - -

-{% endif %} - -{% if changed_settings %} -
    - {% for k, v in changed_settings.items() %} -
  • {{ k }}: {{ v }}
  • - {% endfor %} -
-{% endif %} - -

Continue

- -{% endblock %} diff --git a/app/monitor/templates/home_monitor.html b/app/monitor/templates/home_monitor.html deleted file mode 100644 index 69261aaa..00000000 --- a/app/monitor/templates/home_monitor.html +++ /dev/null @@ -1,40 +0,0 @@ - -{% extends "base.html" %} - -{% block content %} - -
-
- - - - - - -
-

Python Launcher

-

Welcome to the DocuSign Monitor Python examples using JWT flow.

-
- -
-
- -
-

Welcome

-

This launcher includes the following examples for the DocuSign Monitor API.

- - {% if show_doc %} -

Documentation on using JWT from a Python Flask application.

- {% endif %} - -

Basic Monitor API Examples

-

1. Get monitoring data

-

This example demonstrates how to get monitoring data.

-
-
- - - - - -{% endblock %} diff --git a/app/monitor/views/__init__.py b/app/monitor/views/__init__.py new file mode 100644 index 00000000..41b631ee --- /dev/null +++ b/app/monitor/views/__init__.py @@ -0,0 +1 @@ +from .eg001_get_monitoring_data import meg001 diff --git a/app/monitor/views/eg001_get_monitoring_data.py b/app/monitor/views/eg001_get_monitoring_data.py new file mode 100644 index 00000000..94e8e468 --- /dev/null +++ b/app/monitor/views/eg001_get_monitoring_data.py @@ -0,0 +1,62 @@ +"""Example 001: Get monitoring data. """ + +import json +from os import path + +from docusign_monitor.client.api_exception import ApiException +from flask import Blueprint, render_template, current_app, session + +from app.docusign import authenticate, ensure_manifest, get_example_by_number +from app.error_handlers import process_error +from ..examples.eg001_get_monitoring_data import Eg001GetMonitoringDataController +from ...ds_config import DS_CONFIG +from ...consts import API_TYPE + +example_number = 1 +api = API_TYPE["MONITOR"] +eg = f"meg00{example_number}" # Reference (and URL) for this example +meg001 = Blueprint(eg, __name__) + +@meg001.route(f"/{eg}", methods=["POST"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_monitoring_data(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render the response + """ + example = get_example_by_number(session["manifest"], example_number, api) + + # 1. Get required arguments + args = Eg001GetMonitoringDataController.get_args() + try: + # 2. Call the worker method to get your monitor data + results = Eg001GetMonitoringDataController.worker(args) + current_app.logger.info(f"""Got your monitor data""") + except ApiException as err: + return process_error(err) + + return render_template( + "example_done.html", + title=example["ExampleName"], + message="Results from DataSet:getStream method:", + json=json.dumps(json.dumps(results, default=str).replace('\\"', '').replace("'", "")) + ) + +@meg001.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """ Responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "monitor/eg001_get_monitoring_data.html", + title=example["ExampleName"], + example=example, + source_file= "eg001_get_monitoring_data.py", + source_url=DS_CONFIG["monitor_github_url"] + "eg001_get_monitoring_data.py", + documentation=DS_CONFIG["documentation"] + eg, + ) + diff --git a/app/notary/examples/eg004_send_with_third_party_notary.py b/app/notary/examples/eg004_send_with_third_party_notary.py new file mode 100644 index 00000000..f2c67ad9 --- /dev/null +++ b/app/notary/examples/eg004_send_with_third_party_notary.py @@ -0,0 +1,184 @@ +import base64 +from os import path + +from docusign_esign import EnvelopesApi, EnvelopeDefinition, Document, Signer, Notary, SignHere, Tabs, Recipients, \ + NotarySeal, NotaryRecipient, RecipientSignatureProvider, RecipientSignatureProviderOptions + +from ...consts import demo_docs_path, pattern +from ...jwt_helpers import create_api_client + + +class Eg004SendWithThirdPartyNotary: + + @classmethod + def worker(cls, args): + """ + 1. Create the envelope request object + 2. Send the envelope + """ + + envelope_args = args["envelope_args"] + # Create the envelope request object + envelope_definition = cls.make_envelope(envelope_args) + #ds-snippet-start:Notary4Step2 + api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"]) + envelopes_api = EnvelopesApi(api_client) + #ds-snippet-end:Notary4Step2 + + #ds-snippet-start:Notary4Step4 + results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition) + #ds-snippet-end:Notary4Step4 + + envelope_id = results.envelope_id + + return {"envelope_id": envelope_id} + + #ds-snippet-start:Notary4Step3 + @classmethod + def make_envelope(cls, args): + """ + Creates envelope + Document 1: An HTML document. + DocuSign will convert all of the documents to the PDF format. + The recipients" field tags are placed using anchor strings. + """ + + # document 1 (html) has sign here anchor tag **signature_1** + # + # The envelope has two recipients. + # recipient 1 - signer + # The envelope will be sent first to the signer. + + # create the envelope definition + env = EnvelopeDefinition( + email_subject="Please sign this document set" + ) + doc1_b64 = base64.b64encode(bytes(cls.create_document1(args), "utf-8")).decode("ascii") + + # Create the document models + document1 = Document( # create the DocuSign document object + document_base64=doc1_b64, + name="Order acknowledgement", # can be different from actual file name + file_extension="html", # many different document types are accepted + document_id="1" # a label used to reference the doc + ) + # The order in the docs array determines the order in the envelope + env.documents = [document1] + + # Create the signer recipient model + signer1 = Signer( + email=args["signer_email"], + name=args["signer_name"], + recipient_id="2", + routing_order="1", + client_user_id="1000", + notary_id="1" + ) + # routingOrder (lower means earlier) determines the order of deliveries + # to the recipients. Parallel routing order is supported by using the + # same integer as the order for two or more recipients. + + # Create signHere fields (also known as tabs) on the documents, + # We"re using anchor (autoPlace) positioning + # + # The DocuSign platform searches throughout your envelope"s + # documents for matching anchor strings. So the + # signHere2 tab will be used in both document 2 and 3 since they + # use the same anchor string for their "signer 1" tabs. + sign_here1 = SignHere( + document_id="1", + x_position="200", + y_position="235", + page_number="1" + ) + + sign_here2 = SignHere( + stamp_type="stamp", + document_id="1", + x_position="200", + y_position="150", + page_number="1" + + ) + + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object wants arrays of the different field/tab types + signer1.tabs = Tabs(sign_here_tabs=[sign_here1, sign_here2]) + + notary_seal_tab = NotarySeal( + x_position = "300", + y_position = "235", + document_id = "1", + page_number = "1", + ) + + notary_sign_here = SignHere( + x_position = "300", + y_position = "150", + document_id = "1", + page_number = "1", + ) + + notary_tabs = Tabs( + sign_here_tabs = [notary_sign_here], + notary_seal_tabs = [ notary_seal_tab ], + ) + + recipient_signature_provider = RecipientSignatureProvider( + seal_documents_with_tabs_only = "false", + signature_provider_name = "ds_authority_idv", + signature_provider_options = RecipientSignatureProviderOptions() + ) + + notary_recipient = NotaryRecipient( + name = "Notary", + recipient_id = "1", + routing_order = "1", + tabs = notary_tabs, + notary_type = "remote", + notary_source_type = "thirdparty", + notary_third_party_partner = "onenotary", + recipient_signature_providers = [recipient_signature_provider] + ) + + # Add the recipients to the envelope object + recipients = Recipients(signers=[signer1], notaries= [notary_recipient]) + env.recipients = recipients + + # Request that the envelope be sent by setting |status| to "sent". + # To request that the envelope be created as a draft, set to "created" + env.status = args["status"] + + return env + + @classmethod + def create_document1(cls, args): + """ Creates document 1 -- an html document""" + + return f""" + + + + + + +

World Wide Corp

+

Order Processing Division

+

Ordered by {args["signer_name"]}

+

Email: {args["signer_email"]}

+

+ Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. + Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. + Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. + Donut jujubes oat cake jelly-o. + Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake. +

+ +

Agreed: **signature_1**/

+ + + """ + #ds-snippet-end:Notary4Step3 diff --git a/app/notary/views/__init__.py b/app/notary/views/__init__.py new file mode 100644 index 00000000..90384b69 --- /dev/null +++ b/app/notary/views/__init__.py @@ -0,0 +1 @@ +from .eg004_send_with_third_party_notary import neg004 \ No newline at end of file diff --git a/app/notary/views/eg004_send_with_third_party_notary.py b/app/notary/views/eg004_send_with_third_party_notary.py new file mode 100644 index 00000000..de3efbcc --- /dev/null +++ b/app/notary/views/eg004_send_with_third_party_notary.py @@ -0,0 +1,86 @@ +""" Example 004: Send envelope with third party Notary """ + +from os import path + +from docusign_esign.client.api_exception import ApiException +from flask import render_template, session, Blueprint, request + +from ..examples.eg004_send_with_third_party_notary import Eg004SendWithThirdPartyNotary +from ...docusign import authenticate, ensure_manifest, get_example_by_number +from ...ds_config import DS_CONFIG +from ...error_handlers import process_error +from ...consts import pattern, API_TYPE + +example_number = 4 +api = API_TYPE["NOTARY"] +eg = f"neg00{example_number}" # reference (and url) for this example +neg004 = Blueprint(eg, __name__) + +def get_args(): + """Get request and session arguments""" + + # More data validation would be a good idea here + # Strip anything other than characters listed + signer_email = pattern.sub("", request.form.get("signer_email")) + signer_name = pattern.sub("", request.form.get("signer_name")) + + envelope_args = { + "signer_email": signer_email, + "signer_name": signer_name, + "status": "sent", + } + args = { + "account_id": session["ds_account_id"], + "base_path": session["ds_base_path"], + "access_token": session["ds_access_token"], + "envelope_args": envelope_args + } + return args + +@neg004.route(f"/{eg}", methods=["POST"]) +@authenticate(eg=eg, api=api) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +def sign_by_email(): + """ + 1. Get required arguments + 2. Call the worker method + 3. Render success response with envelopeId + """ + + # 1. Get required arguments + #args = Eg002SigningViaEmailController.get_args() + args = get_args() + try: + # 1. Call the worker method + results = Eg004SendWithThirdPartyNotary.worker(args) + except ApiException as err: + return process_error(err) + + session["envelope_id"] = results["envelope_id"] # Save for use by other examples which need an envelopeId + + # 2. Render success response with envelopeId + example = get_example_by_number(session["manifest"], example_number, api) + return render_template( + "example_done.html", + title=example["ExampleName"], + message=example["ResultsPageText"].format(results['envelope_id']) + ) + +@neg004.route(f"/{eg}", methods=["GET"]) +@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"]) +@authenticate(eg=eg, api=api) +def get_view(): + """responds with the form for the example""" + example = get_example_by_number(session["manifest"], example_number, api) + + return render_template( + "notary/eg004_send_with_third_party_notary.html", + title=example["ExampleName"], + example=example, + source_file="eg004_send_with_third_party_notary.py", + source_url=DS_CONFIG["github_example_url"] + "eg004_send_with_third_party_notary.py", + documentation=DS_CONFIG["documentation"] + eg, + show_doc=DS_CONFIG["documentation"], + signer_name=DS_CONFIG["signer_name"], + signer_email=DS_CONFIG["signer_email"] + ) diff --git a/app/quick_acg/quick_acg_app/__init__.py b/app/quick_acg/quick_acg_app/__init__.py new file mode 100644 index 00000000..ac775eba --- /dev/null +++ b/app/quick_acg/quick_acg_app/__init__.py @@ -0,0 +1,34 @@ +import os + +from flask import Flask +from flask_wtf.csrf import CSRFProtect + +from app.eg001_embedded_signing import eg001 +from app.eSignature.views.eg041_cfr_embedded_signing import eg041 +from .views import core, ds + +session_path = "/tmp/python_recipe_sessions" + +quick_acg_app = Flask(__name__) +quick_acg_app.config.from_pyfile("../../config.py") + +# See https://flask-wtf.readthedocs.io/en/stable/csrf.html +csrf = CSRFProtect(quick_acg_app) + +# Register home page +quick_acg_app.register_blueprint(core) + +# Register OAuth +quick_acg_app.register_blueprint(ds) +# Register examples +quick_acg_app.register_blueprint(eg001) +quick_acg_app.register_blueprint(eg041) + +if "DYNO" in os.environ: # On Heroku? + import logging + + stream_handler = logging.StreamHandler() + quick_acg_app.logger.addHandler(stream_handler) + quick_acg_app.logger.setLevel(logging.INFO) + quick_acg_app.logger.info("Recipe example startup") + quick_acg_app.config.update(dict(PREFERRED_URL_SCHEME="https")) diff --git a/app/rooms/templates/base.html b/app/quick_acg/quick_acg_app/templates/base.html similarity index 81% rename from app/rooms/templates/base.html rename to app/quick_acg/quick_acg_app/templates/base.html index 878232d8..6e1b71b6 100644 --- a/app/rooms/templates/base.html +++ b/app/quick_acg/quick_acg_app/templates/base.html @@ -5,7 +5,7 @@ - + {{ title }} @@ -15,7 +15,7 @@