From 85deeee9adc278ab40ed40ffe4ee48ee9b62f9ba Mon Sep 17 00:00:00 2001
From: RomanBachaloSigmaSoftware
<77355790+RomanBachaloSigmaSoftware@users.noreply.github.com>
Date: Fri, 22 Mar 2024 22:52:40 +0200
Subject: [PATCH] Added maestro code examples (#142)
* added examples
* updates and fixes
* remove redundant scope
* update example 1
* adding codeDepot markers
---------
Co-authored-by: Paige Rossi
---
.gitignore | 3 +
app/__init__.py | 5 +
app/consts.py | 3 +
app/docusign/ds_client.py | 8 +
app/docusign/utils.py | 12 +-
app/ds_config_sample.py | 1 +
app/maestro/__init__.py | 3 +
.../examples/eg001_trigger_workflow.py | 75 +++
app/maestro/examples/eg002_cancel_workflow.py | 45 ++
.../examples/eg003_get_workflow_status.py | 35 ++
app/maestro/utils.py | 584 ++++++++++++++++++
app/maestro/views/__init__.py | 3 +
app/maestro/views/eg001_trigger_workflow.py | 184 ++++++
app/maestro/views/eg002_cancel_workflow.py | 103 +++
.../views/eg003_get_workflow_status.py | 76 +++
app/static/assets/search.js | 3 +
app/templates/error.html | 2 +-
app/templates/home.html | 2 +-
.../maestro/eg001_publish_workflow.html | 11 +
.../maestro/eg001_trigger_workflow.html | 59 ++
.../maestro/eg002_cancel_workflow.html | 38 ++
.../maestro/eg003_get_workflow_status.html | 38 ++
22 files changed, 1290 insertions(+), 3 deletions(-)
create mode 100644 app/maestro/__init__.py
create mode 100644 app/maestro/examples/eg001_trigger_workflow.py
create mode 100644 app/maestro/examples/eg002_cancel_workflow.py
create mode 100644 app/maestro/examples/eg003_get_workflow_status.py
create mode 100644 app/maestro/utils.py
create mode 100644 app/maestro/views/__init__.py
create mode 100644 app/maestro/views/eg001_trigger_workflow.py
create mode 100644 app/maestro/views/eg002_cancel_workflow.py
create mode 100644 app/maestro/views/eg003_get_workflow_status.py
create mode 100644 app/templates/maestro/eg001_publish_workflow.html
create mode 100644 app/templates/maestro/eg001_trigger_workflow.html
create mode 100644 app/templates/maestro/eg002_cancel_workflow.html
create mode 100644 app/templates/maestro/eg003_get_workflow_status.html
diff --git a/.gitignore b/.gitignore
index 73a0dc0..101b3e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,3 +123,6 @@ private.key
# Current flask session
flask_session/
+
+# Workflow ID file
+WORKFLOW_ID.txt
\ No newline at end of file
diff --git a/app/__init__.py b/app/__init__.py
index ac472b8..39f35b4 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -12,6 +12,7 @@
from .monitor import views as monitor_views
from .admin import views as admin_views
from .connect import views as connect_views
+from .maestro import views as maestro_views
from .webforms import views as webforms_views
from .views import core
@@ -114,6 +115,10 @@
app.register_blueprint(connect_views.cneg001)
+app.register_blueprint(maestro_views.mseg001)
+app.register_blueprint(maestro_views.mseg002)
+app.register_blueprint(maestro_views.mseg003)
+
app.register_blueprint(webforms_views.weg001)
if "DYNO" in os.environ: # On Heroku?
diff --git a/app/consts.py b/app/consts.py
index 532015f..530ce45 100644
--- a/app/consts.py
+++ b/app/consts.py
@@ -29,6 +29,8 @@
# Base uri for callback function
base_uri_suffix = "/restapi"
+# Workflow name
+workflow_name = "Example workflow - send invite to signer"
# Default languages for brand
languages = {
@@ -114,5 +116,6 @@
"ROOMS": "Rooms",
"ADMIN": "Admin",
"CONNECT": "Connect",
+ "MAESTRO": "Maestro",
"WEBFORMS": "WebForms"
}
diff --git a/app/docusign/ds_client.py b/app/docusign/ds_client.py
index 384194e..348b4b4 100644
--- a/app/docusign/ds_client.py
+++ b/app/docusign/ds_client.py
@@ -33,6 +33,10 @@
"asset_group_account_read", "asset_group_account_clone_write", "asset_group_account_clone_read"
]
+MAESTRO_SCOPES = [
+ "signature", "aow_manage"
+]
+
WEBFORMS_SCOPES = [
"signature", "webforms_read", "webforms_instance_read", "webforms_instance_write"
]
@@ -61,6 +65,8 @@ def _auth_code_grant(cls, api):
use_scopes.extend(CLICK_SCOPES)
elif api == "Admin":
use_scopes.extend(ADMIN_SCOPES)
+ elif api == "Maestro":
+ use_scopes.extend(MAESTRO_SCOPES)
elif api == "WebForms":
use_scopes.extend(WEBFORMS_SCOPES)
else:
@@ -99,6 +105,8 @@ def _jwt_auth(cls, api):
use_scopes.extend(CLICK_SCOPES)
elif api == "Admin":
use_scopes.extend(ADMIN_SCOPES)
+ elif api == "Maestro":
+ use_scopes.extend(MAESTRO_SCOPES)
elif api == "WebForms":
use_scopes.extend(WEBFORMS_SCOPES)
else:
diff --git a/app/docusign/utils.py b/app/docusign/utils.py
index 730751f..eefc0c0 100644
--- a/app/docusign/utils.py
+++ b/app/docusign/utils.py
@@ -1,7 +1,7 @@
from datetime import timedelta, datetime
from functools import wraps
import requests
-import urllib
+from urllib.parse import urlparse, parse_qs
import json
import re
@@ -148,6 +148,16 @@ def get_user_info(access_token, base_path, oauth_host_name):
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()
diff --git a/app/ds_config_sample.py b/app/ds_config_sample.py
index 2d04866..b18d165 100644
--- a/app/ds_config_sample.py
+++ b/app/ds_config_sample.py
@@ -16,6 +16,7 @@
"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",
+ "maestro_api_client_host": "https://demo.services.docusign.net/",
"webforms_api_client_host": "https://apps-d.docusign.com/api/webforms/v1.1",
"allow_silent_authentication": True, # a user can be silently authenticated if they have an
# active login session on another tab of the same browser
diff --git a/app/maestro/__init__.py b/app/maestro/__init__.py
new file mode 100644
index 0000000..1697d73
--- /dev/null
+++ b/app/maestro/__init__.py
@@ -0,0 +1,3 @@
+from .views import mseg001
+from .views import mseg002
+from .views import mseg003
diff --git a/app/maestro/examples/eg001_trigger_workflow.py b/app/maestro/examples/eg001_trigger_workflow.py
new file mode 100644
index 0000000..7a99cac
--- /dev/null
+++ b/app/maestro/examples/eg001_trigger_workflow.py
@@ -0,0 +1,75 @@
+from docusign_maestro import WorkflowManagementApi, WorkflowTriggerApi, TriggerPayload
+from flask import session, request
+
+from app.docusign.utils import get_parameter_value_from_url
+from app.ds_config import DS_CONFIG
+from app.maestro.utils import create_maestro_api_client
+from app.consts import pattern
+
+
+class Eg001TriggerWorkflowController:
+ @staticmethod
+ def get_args():
+ """Get request and session arguments"""
+ return {
+ "account_id": session["ds_account_id"],
+ "base_path": DS_CONFIG["maestro_api_client_host"],
+ "access_token": session["ds_access_token"],
+ "workflow_id": session["workflow_id"],
+ "instance_name": pattern.sub("", request.form.get("instance_name")),
+ "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")),
+ }
+
+ @staticmethod
+ def get_workflow_definitions(args):
+ api_client = create_maestro_api_client(args["base_path"], args["access_token"])
+ workflow_management_api = WorkflowManagementApi(api_client)
+ workflow_definitions = workflow_management_api.get_workflow_definitions(args["account_id"], status="active")
+
+ return workflow_definitions
+
+ @staticmethod
+ def get_workflow_definition(args):
+ #ds-snippet-start:Maestro1Step2
+ api_client = create_maestro_api_client(args["base_path"], args["access_token"])
+ #ds-snippet-end:Maestro1Step2
+
+ #ds-snippet-start:Maestro1Step3
+ workflow_management_api = WorkflowManagementApi(api_client)
+ workflow_definition = workflow_management_api.get_workflow_definition(args["account_id"], args["workflow_id"])
+ #ds-snippet-end:Maestro1Step3
+
+ return workflow_definition
+
+ @staticmethod
+ def trigger_workflow(workflow, args):
+ api_client = create_maestro_api_client(args["base_path"], args["access_token"])
+
+ #ds-snippet-start:Maestro1Step4
+ trigger_payload = TriggerPayload(
+ instance_name=args["instance_name"],
+ participant={},
+ payload={
+ "signerEmail": args["signer_email"],
+ "signerName": args["signer_name"],
+ "ccEmail": args["cc_email"],
+ "ccName": args["cc_name"]
+ },
+ metadata={}
+ )
+ mtid = get_parameter_value_from_url(workflow.trigger_url, "mtid")
+ mtsec = get_parameter_value_from_url(workflow.trigger_url, "mtsec")
+ #ds-snippet-end:Maestro1Step4
+
+ #ds-snippet-start:Maestro1Step5
+ workflow_trigger_api = WorkflowTriggerApi(api_client)
+ trigger_response = workflow_trigger_api.trigger_workflow(
+ args["account_id"],
+ trigger_payload,
+ mtid=mtid, mtsec=mtsec
+ )
+ #ds-snippet-end:Maestro1Step5
+ return trigger_response
diff --git a/app/maestro/examples/eg002_cancel_workflow.py b/app/maestro/examples/eg002_cancel_workflow.py
new file mode 100644
index 0000000..4425ef1
--- /dev/null
+++ b/app/maestro/examples/eg002_cancel_workflow.py
@@ -0,0 +1,45 @@
+from docusign_maestro import WorkflowInstanceManagementApi
+from flask import session
+
+from app.ds_config import DS_CONFIG
+from app.maestro.utils import create_maestro_api_client
+
+
+class Eg002CancelWorkflowController:
+ @staticmethod
+ def get_args():
+ """Get request and session arguments"""
+ return {
+ "account_id": session["ds_account_id"],
+ "base_path": DS_CONFIG["maestro_api_client_host"],
+ "access_token": session["ds_access_token"],
+ "workflow_id": session["workflow_id"],
+ "instance_id": session["instance_id"]
+ }
+
+ @staticmethod
+ def get_instance_state(args):
+ api_client = create_maestro_api_client(args["base_path"], args["access_token"])
+ workflow_instance_management_api = WorkflowInstanceManagementApi(api_client)
+ instance = workflow_instance_management_api.get_workflow_instance(
+ args["account_id"],
+ args["workflow_id"],
+ args["instance_id"]
+ )
+
+ return instance.instance_state
+
+ @staticmethod
+ def cancel_workflow_instance(args):
+ #ds-snippet-start:Maestro2Step2
+ api_client = create_maestro_api_client(args["base_path"], args["access_token"])
+ #ds-snippet-end:Maestro2Step2
+
+ #ds-snippet-start:Maestro2Step3
+ workflow_instance_management_api = WorkflowInstanceManagementApi(api_client)
+ cancel_result = workflow_instance_management_api.cancel_workflow_instance(
+ args["account_id"],
+ args["instance_id"]
+ )
+ #ds-snippet-end:Maestro2Step3
+ return cancel_result
diff --git a/app/maestro/examples/eg003_get_workflow_status.py b/app/maestro/examples/eg003_get_workflow_status.py
new file mode 100644
index 0000000..4777f0a
--- /dev/null
+++ b/app/maestro/examples/eg003_get_workflow_status.py
@@ -0,0 +1,35 @@
+from docusign_maestro import WorkflowInstanceManagementApi
+from flask import session
+
+from app.ds_config import DS_CONFIG
+from app.maestro.utils import create_maestro_api_client
+
+
+class Eg003GetWorkflowStatusController:
+ @staticmethod
+ def get_args():
+ """Get request and session arguments"""
+ return {
+ "account_id": session["ds_account_id"],
+ "base_path": DS_CONFIG["maestro_api_client_host"],
+ "access_token": session["ds_access_token"],
+ "workflow_id": session["workflow_id"],
+ "instance_id": session["instance_id"]
+ }
+
+ @staticmethod
+ def get_workflow_instance(args):
+ #ds-snippet-start:Maestro3Step2
+ api_client = create_maestro_api_client(args["base_path"], args["access_token"])
+ #ds-snippet-end:Maestro3Step2
+
+ #ds-snippet-start:Maestro3Step3
+ workflow_instance_management_api = WorkflowInstanceManagementApi(api_client)
+ instance = workflow_instance_management_api.get_workflow_instance(
+ args["account_id"],
+ args["workflow_id"],
+ args["instance_id"]
+ )
+ #ds-snippet-end:Maestro3Step3
+
+ return instance
diff --git a/app/maestro/utils.py b/app/maestro/utils.py
new file mode 100644
index 0000000..a44125d
--- /dev/null
+++ b/app/maestro/utils.py
@@ -0,0 +1,584 @@
+import uuid
+from docusign_maestro import ApiClient, WorkflowManagementApi, WorkflowDefinition, DeployRequest, \
+ DSWorkflowTrigger, DSWorkflowVariableFromVariable, DeployStatus
+
+import json
+
+
+def create_maestro_api_client(base_path, access_token):
+ 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
+
+
+def create_workflow(args):
+ signer_id = str(uuid.uuid4())
+ cc_id = str(uuid.uuid4())
+ trigger_id = "wfTrigger"
+
+ participants = {
+ signer_id: {
+ "participantRole": "Signer"
+ },
+ cc_id: {
+ "participantRole": "CC"
+ }
+ }
+
+ dac_id_field = f"dacId_{trigger_id}"
+ id_field = f"id_{trigger_id}"
+ signer_name_field = f"signerName_{trigger_id}"
+ signer_email_field = f"signerEmail_{trigger_id}"
+ cc_name_field = f"ccName_{trigger_id}"
+ cc_email_field = f"ccEmail_{trigger_id}"
+
+ trigger = DSWorkflowTrigger(
+ name="Get_URL",
+ type="Http",
+ http_type="Get",
+ id=trigger_id,
+ input={
+ 'metadata': {
+ 'customAttributes': {}
+ },
+ 'payload': {
+ dac_id_field: {
+ 'source': 'step',
+ 'propertyName': 'dacId',
+ 'stepId': trigger_id
+ },
+ id_field: {
+ 'source': 'step',
+ 'propertyName': 'id',
+ 'stepId': trigger_id
+ },
+ signer_name_field: {
+ 'source': 'step',
+ 'propertyName': 'signerName',
+ 'stepId': trigger_id
+ },
+ signer_email_field: {
+ 'source': 'step',
+ 'propertyName': 'signerEmail',
+ 'stepId': trigger_id
+ },
+ cc_name_field: {
+ 'source': 'step',
+ 'propertyName': 'ccName',
+ 'stepId': trigger_id
+ },
+ cc_email_field: {
+ 'source': 'step',
+ 'propertyName': 'ccEmail',
+ 'stepId': trigger_id
+ }
+ },
+ 'participants': {}
+ },
+ output={
+ dac_id_field: {
+ 'source': 'step',
+ 'propertyName': 'dacId',
+ 'stepId': trigger_id
+ }
+ }
+ )
+
+ variables = {
+ dac_id_field: DSWorkflowVariableFromVariable(source='step', property_name='dacId', step_id=trigger_id),
+ id_field: DSWorkflowVariableFromVariable(source='step', property_name='id', step_id=trigger_id),
+ signer_name_field: DSWorkflowVariableFromVariable(source='step', property_name='signerName',
+ step_id=trigger_id),
+ signer_email_field: DSWorkflowVariableFromVariable(source='step', property_name='signerEmail',
+ step_id=trigger_id),
+ cc_name_field: DSWorkflowVariableFromVariable(source='step', property_name='ccName', step_id=trigger_id),
+ cc_email_field: DSWorkflowVariableFromVariable(source='step', property_name='ccEmail', step_id=trigger_id),
+ 'envelopeId_step2': DSWorkflowVariableFromVariable(source='step', property_name='envelopeId', step_id='step2',
+ type='String'),
+ 'combinedDocumentsBase64_step2': DSWorkflowVariableFromVariable(source='step',
+ property_name='combinedDocumentsBase64',
+ step_id='step2', type='File'),
+ 'fields.signer.text.value_step2': DSWorkflowVariableFromVariable(source='step',
+ property_name='fields.signer.text.value',
+ step_id='step2', type='String')
+ }
+
+ step1 = {
+ 'id': 'step1',
+ 'name': 'Set Up Invite',
+ 'moduleName': 'Notification-SendEmail',
+ 'configurationProgress': 'Completed',
+ 'type': 'DS-EmailNotification',
+ 'config': {
+ 'templateType': 'WorkflowParticipantNotification',
+ 'templateVersion': 1,
+ 'language': 'en',
+ 'sender_name': 'DocuSign Orchestration',
+ 'sender_alias': 'Orchestration',
+ 'participantId': signer_id
+ },
+ 'input': {
+ 'recipients': [
+ {
+ 'name': {
+ 'source': 'step',
+ 'propertyName': 'signerName',
+ 'stepId': trigger_id
+ },
+ 'email': {
+ 'source': 'step',
+ 'propertyName': 'signerEmail',
+ 'stepId': trigger_id
+ }
+ }
+ ],
+ 'mergeValues': {
+ 'CustomMessage': 'Follow this link to access and complete the workflow.',
+ 'ParticipantFullName': {
+ 'source': 'step',
+ 'propertyName': 'signerName',
+ 'stepId': trigger_id
+ }
+ }
+ },
+ 'output': {}
+ }
+
+ step2 = {
+ "id": 'step2',
+ "name": 'Get Signatures',
+ "moduleName": 'ESign',
+ "configurationProgress": 'Completed',
+ "type": 'DS-Sign',
+ "config": {
+ "participantId": signer_id,
+ },
+ "input": {
+ "isEmbeddedSign": True,
+ "documents": [
+ {
+ "type": 'FromDSTemplate',
+ "eSignTemplateId": args["template_id"],
+ },
+ ],
+ "emailSubject": 'Please sign this document',
+ "emailBlurb": '',
+ "recipients": {
+ "signers": [
+ {
+ "defaultRecipient": 'false',
+ "tabs": {
+ "signHereTabs": [
+ {
+ "stampType": 'signature',
+ "name": 'SignHere',
+ "tabLabel": 'Sign Here',
+ "scaleValue": '1',
+ "optional": 'false',
+ "documentId": '1',
+ "recipientId": '1',
+ "pageNumber": '1',
+ "xPosition": '191',
+ "yPosition": '148',
+ "tabId": '1',
+ "tabType": 'signhere',
+ },
+ ],
+ 'textTabs': [
+ {
+ "requireAll": 'false',
+ "value": '',
+ "required": 'false',
+ "locked": 'false',
+ "concealValueOnDocument": 'false',
+ "disableAutoSize": 'false',
+ "tabLabel": 'text',
+ "font": 'helvetica',
+ "fontSize": 'size14',
+ "localePolicy": {},
+ "documentId": '1',
+ "recipientId": '1',
+ "pageNumber": '1',
+ "xPosition": '153',
+ "yPosition": '230',
+ "width": '84',
+ "height": '23',
+ "tabId": '2',
+ "tabType": 'text',
+ },
+ ],
+ "checkboxTabs": [
+ {
+ "name": '',
+ "tabLabel": 'ckAuthorization',
+ "selected": 'false',
+ "selectedOriginal": 'false',
+ "requireInitialOnSharedChange": 'false',
+ "required": 'true',
+ "locked": 'false',
+ "documentId": '1',
+ "recipientId": '1',
+ "pageNumber": '1',
+ "xPosition": '75',
+ "yPosition": '417',
+ "width": '0',
+ "height": '0',
+ "tabId": '3',
+ "tabType": 'checkbox',
+ },
+ {
+ "name": '',
+ "tabLabel": 'ckAuthentication',
+ "selected": 'false',
+ "selectedOriginal": 'false',
+ "requireInitialOnSharedChange": 'false',
+ "required": 'true',
+ "locked": 'false',
+ "documentId": '1',
+ "recipientId": '1',
+ "pageNumber": '1',
+ "xPosition": '75',
+ "yPosition": '447',
+ "width": '0',
+ "height": '0',
+ "tabId": '4',
+ "tabType": 'checkbox',
+ },
+ {
+ "name": '',
+ "tabLabel": 'ckAgreement',
+ "selected": 'false',
+ "selectedOriginal": 'false',
+ "requireInitialOnSharedChange": 'false',
+ "required": 'true',
+ "locked": 'false',
+ "documentId": '1',
+ "recipientId": '1',
+ "pageNumber": '1',
+ "xPosition": '75',
+ "yPosition": '478',
+ "width": '0',
+ "height": '0',
+ "tabId": '5',
+ "tabType": 'checkbox',
+ },
+ {
+ "name": '',
+ "tabLabel": 'ckAcknowledgement',
+ "selected": 'false',
+ "selectedOriginal": 'false',
+ "requireInitialOnSharedChange": 'false',
+ "required": 'true',
+ "locked": 'false',
+ "documentId": '1',
+ "recipientId": '1',
+ "pageNumber": '1',
+ "xPosition": '75',
+ "yPosition": '508',
+ "width": '0',
+ "height": '0',
+ "tabId": '6',
+ "tabType": 'checkbox',
+ },
+ ],
+ "radioGroupTabs": [
+ {
+ "documentId": '1',
+ "recipientId": '1',
+ "groupName": 'radio1',
+ "radios": [
+ {
+ "pageNumber": '1',
+ "xPosition": '142',
+ "yPosition": '384',
+ "value": 'white',
+ "selected": 'false',
+ "tabId": '7',
+ "required": 'false',
+ "locked": 'false',
+ "bold": 'false',
+ "italic": 'false',
+ "underline": 'false',
+ "fontColor": 'black',
+ "fontSize": 'size7',
+ },
+ {
+ "pageNumber": '1',
+ "xPosition": '74',
+ "yPosition": '384',
+ "value": 'red',
+ "selected": 'false',
+ "tabId": '8',
+ "required": 'false',
+ "locked": 'false',
+ "bold": 'false',
+ "italic": 'false',
+ "underline": 'false',
+ "fontColor": 'black',
+ "fontSize": 'size7',
+ },
+ {
+ "pageNumber": '1',
+ "xPosition": '220',
+ "yPosition": '384',
+ "value": 'blue',
+ "selected": 'false',
+ "tabId": '9',
+ "required": 'false',
+ "locked": 'false',
+ "bold": 'false',
+ "italic": 'false',
+ "underline": 'false',
+ "fontColor": 'black',
+ "fontSize": 'size7',
+ },
+ ],
+ "shared": 'false',
+ "requireInitialOnSharedChange": 'false',
+ "requireAll": 'false',
+ "tabType": 'radiogroup',
+ "value": '',
+ "originalValue": '',
+ },
+ ],
+ "listTabs": [
+ {
+ "listItems": [
+ {
+ "text": 'Red',
+ "value": 'red',
+ "selected": 'false',
+ },
+ {
+ "text": 'Orange',
+ "value": 'orange',
+ "selected": 'false',
+ },
+ {
+ "text": 'Yellow',
+ "value": 'yellow',
+ "selected": 'false',
+ },
+ {
+ "text": 'Green',
+ "value": 'green',
+ "selected": 'false',
+ },
+ {
+ "text": 'Blue',
+ "value": 'blue',
+ "selected": 'false',
+ },
+ {
+ "text": 'Indigo',
+ "value": 'indigo',
+ "selected": 'false',
+ },
+ {
+ "text": 'Violet',
+ "value": 'violet',
+ "selected": 'false',
+ },
+ ],
+ "value": '',
+ "originalValue": '',
+ "required": 'false',
+ "locked": 'false',
+ "requireAll": 'false',
+ "tabLabel": 'list',
+ "font": 'helvetica',
+ "fontSize": 'size14',
+ "localePolicy": {},
+ "documentId": '1',
+ "recipientId": '1',
+ "pageNumber": '1',
+ "xPosition": '142',
+ "yPosition": '291',
+ "width": '78',
+ "height": '0',
+ "tabId": '10',
+ "tabType": 'list',
+ },
+ ],
+ "numericalTabs": [
+ {
+ "validationType": 'currency',
+ "value": '',
+ "required": 'false',
+ "locked": 'false',
+ "concealValueOnDocument": 'false',
+ "disableAutoSize": 'false',
+ "tabLabel": 'numericalCurrency',
+ "font": 'helvetica',
+ "fontSize": 'size14',
+ "localePolicy": {
+ "cultureName": 'en-US',
+ "currencyPositiveFormat":
+ 'csym_1_comma_234_comma_567_period_89',
+ "currencyNegativeFormat":
+ 'opar_csym_1_comma_234_comma_567_period_89_cpar',
+ "currencyCode": 'usd',
+ },
+ "documentId": '1',
+ "recipientId": '1',
+ "pageNumber": '1',
+ "xPosition": '163',
+ "yPosition": '260',
+ "width": '84',
+ "height": '0',
+ "tabId": '11',
+ "tabType": 'numerical',
+ },
+ ],
+ },
+ "signInEachLocation": 'false',
+ "agentCanEditEmail": 'false',
+ "agentCanEditName": 'false',
+ "requireUploadSignature": 'false',
+ "name": {
+ "source": 'step',
+ "propertyName": 'signerName',
+ "stepId": trigger_id,
+ },
+ "email": {
+ "source": 'step',
+ "propertyName": 'signerEmail',
+ "stepId": trigger_id,
+ },
+ "recipientId": '1',
+ "recipientIdGuid": '00000000-0000-0000-0000-000000000000',
+ "accessCode": '',
+ "requireIdLookup": 'false',
+ "routingOrder": '1',
+ "note": '',
+ "roleName": 'signer',
+ "completedCount": '0',
+ "deliveryMethod": 'email',
+ "templateLocked": 'false',
+ "templateRequired": 'false',
+ "inheritEmailNotificationConfiguration": 'false',
+ "recipientType": 'signer',
+ },
+ ],
+ "carbonCopies": [
+ {
+ "agentCanEditEmail": 'false',
+ "agentCanEditName": 'false',
+ "name": {
+ "source": 'step',
+ "propertyName": 'ccName',
+ "stepId": trigger_id,
+ },
+ "email": {
+ "source": 'step',
+ "propertyName": 'ccEmail',
+ "stepId": trigger_id,
+ },
+ "recipientId": '2',
+ "recipientIdGuid": '00000000-0000-0000-0000-000000000000',
+ "accessCode": '',
+ "requireIdLookup": 'false',
+ "routingOrder": '2',
+ "note": '',
+ "roleName": 'cc',
+ "completedCount": '0',
+ "deliveryMethod": 'email',
+ "templateLocked": 'false',
+ "templateRequired": 'false',
+ "inheritEmailNotificationConfiguration": 'false',
+ "recipientType": 'carboncopy',
+ },
+ ],
+ "certifiedDeliveries": [],
+ },
+ },
+ "output": {
+ "envelopeId_step2": {
+ "source": 'step',
+ "propertyName": 'envelopeId',
+ "stepId": 'step2',
+ "type": 'String',
+ },
+ "combinedDocumentsBase64_step2": {
+ "source": 'step',
+ "propertyName": 'combinedDocumentsBase64',
+ "stepId": 'step2',
+ "type": 'File',
+ },
+ 'fields.signer.text.value_step2': {
+ "source": 'step',
+ "propertyName": 'fields.signer.text.value',
+ "stepId": 'step2',
+ "type": 'String',
+ },
+ },
+ }
+
+ step3 = {
+ "id": 'step3',
+ "name": 'Show a Confirmation Screen',
+ "moduleName": 'ShowConfirmationScreen',
+ "configurationProgress": 'Completed',
+ "type": 'DS-ShowScreenStep',
+ "config": {
+ "participantId": signer_id
+ },
+ "input": {
+ "httpType": "Post",
+ "payload": {
+ "participantId": signer_id,
+ "confirmationMessage": {
+ "title": 'Tasks complete',
+ "description": 'You have completed all your workflow tasks.'
+ }
+ }
+ },
+ "output": {}
+ }
+
+ workflow_definition = WorkflowDefinition(
+ workflow_name="Example workflow - send invite to signer",
+ workflow_description="",
+ document_version="1.0.0",
+ schema_version="1.0.0",
+ account_id=args["account_id"],
+ participants=participants,
+ trigger=trigger,
+ variables=variables,
+ steps=[step1, step2, step3]
+ )
+
+ api_client = create_maestro_api_client(args["base_path"], args["access_token"])
+ workflow_management_api = WorkflowManagementApi(api_client)
+ # body = {"workflowDefinition": workflow_definition.__dict__}
+ workflow = workflow_management_api.create_workflow_definition(
+ args["account_id"],
+ {"workflowDefinition": workflow_definition}
+ )
+
+ return workflow.workflow_definition_id
+
+
+def publish_workflow(args, workflow_id):
+ api_client = create_maestro_api_client(args["base_path"], args["access_token"])
+ workflow_management_api = WorkflowManagementApi(api_client)
+
+ try:
+ deploy_request = DeployRequest(
+ deployment_status=DeployStatus.PUBLISH
+ )
+ workflow_management_api.publish_or_un_publish_workflow_definition(
+ args["account_id"],
+ workflow_id,
+ deploy_request
+ )
+ except Exception as err:
+ if hasattr(err, 'response') and hasattr(err.response, 'data'):
+ response_data = json.loads(err.response.data)
+ if 'message' in response_data:
+ is_consent_required = response_data['message'] == 'Consent required'
+ if is_consent_required:
+ return response_data["consentUrl"]
+ raise err
diff --git a/app/maestro/views/__init__.py b/app/maestro/views/__init__.py
new file mode 100644
index 0000000..93520d8
--- /dev/null
+++ b/app/maestro/views/__init__.py
@@ -0,0 +1,3 @@
+from .eg001_trigger_workflow import mseg001
+from .eg002_cancel_workflow import mseg002
+from .eg003_get_workflow_status import mseg003
diff --git a/app/maestro/views/eg001_trigger_workflow.py b/app/maestro/views/eg001_trigger_workflow.py
new file mode 100644
index 0000000..8df42bd
--- /dev/null
+++ b/app/maestro/views/eg001_trigger_workflow.py
@@ -0,0 +1,184 @@
+"""Example 001: How to trigger a Maestro workflow"""
+
+import json
+
+from docusign_maestro.client.api_exception import ApiException
+from flask import render_template, Blueprint, session
+
+from ..examples.eg001_trigger_workflow import Eg001TriggerWorkflowController
+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 ..utils import create_workflow, publish_workflow
+
+example_number = 1
+api = API_TYPE["MAESTRO"]
+eg = f"mseg00{example_number}" # reference (and url) for this example
+mseg001 = Blueprint(eg, __name__)
+
+
+@mseg001.route(f"/{eg}", methods=["POST"])
+@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"])
+@authenticate(eg=eg, api=api)
+def trigger_workflow():
+ """
+ 1. Get required arguments
+ 2. Call the worker method
+ 3. Show results
+ """
+ example = get_example_by_number(session["manifest"], example_number, api)
+
+ # 1. Get required arguments
+ args = Eg001TriggerWorkflowController.get_args()
+ try:
+ # 1. Call the worker method
+ print("args:\n\n")
+ print(args)
+ workflow = Eg001TriggerWorkflowController.get_workflow_definition(args)
+ results = Eg001TriggerWorkflowController.trigger_workflow(workflow, args)
+ session["instance_id"] = results.instance_id
+ except ApiException as err:
+ if hasattr(err, "status"):
+ if err.status == 403:
+ return render_template(
+ "error.html",
+ err=err,
+ error_code=err.status,
+ error_message=session["manifest"]["SupportingTexts"]["ContactSupportToEnableFeature"]
+ .format("Maestro")
+ )
+
+ return process_error(err)
+ # 3. Show results
+ return render_template(
+ "example_done.html",
+ title=example["ExampleName"],
+ message=example["ResultsPageText"],
+ json=json.dumps(json.dumps(results.to_dict()))
+ )
+
+
+@mseg001.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)
+ additional_page_data = next((p for p in example["AdditionalPage"] if p["Name"] == "publish_workflow"), None)
+
+ args = {
+ "account_id": session["ds_account_id"],
+ "base_path": DS_CONFIG["maestro_api_client_host"],
+ "access_token": session["ds_access_token"],
+ "template_id": session.get("template_id", None)
+ }
+ try:
+ workflows = Eg001TriggerWorkflowController.get_workflow_definitions(args)
+
+ if workflows.count > 0:
+ sorted_workflows = sorted(
+ workflows.value,
+ key=lambda w: w.last_updated_date,
+ reverse=True
+ )
+
+ if sorted_workflows:
+ session["workflow_id"] = sorted_workflows[0].id
+
+ if "workflow_id" not in session:
+ if "template_id" not in session:
+ return render_template(
+ "maestro/eg001_trigger_workflow.html",
+ title=example["ExampleName"],
+ example=example,
+ template_ok=False,
+ source_file="eg001_trigger_workflow.py",
+ source_url=DS_CONFIG["github_example_url"] + "eg001_trigger_workflow.py",
+ documentation=DS_CONFIG["documentation"] + eg,
+ show_doc=DS_CONFIG["documentation"],
+ )
+
+ # if there is no workflow, then create one
+ session["workflow_id"] = create_workflow(args)
+ consent_url = publish_workflow(args, session["workflow_id"])
+
+ if consent_url:
+ return render_template(
+ "maestro/eg001_publish_workflow.html",
+ title=example["ExampleName"],
+ message=additional_page_data["ResultsPageText"],
+ consent_url=consent_url
+ )
+
+ except ApiException as err:
+ if hasattr(err, "status"):
+ if err.status == 403:
+ return render_template(
+ "error.html",
+ err=err,
+ error_code=err.status,
+ error_message=session["manifest"]["SupportingTexts"]["ContactSupportToEnableFeature"]
+ .format("Maestro")
+ )
+
+ return process_error(err)
+
+ return render_template(
+ "maestro/eg001_trigger_workflow.html",
+ title=example["ExampleName"],
+ example=example,
+ template_ok=True,
+ source_file="eg001_trigger_workflow.py",
+ source_url=DS_CONFIG["github_example_url"] + "eg001_trigger_workflow.py",
+ documentation=DS_CONFIG["documentation"] + eg,
+ show_doc=DS_CONFIG["documentation"],
+ )
+
+@mseg001.route(f"/{eg}publish", methods=["POST"])
+@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"])
+@authenticate(eg=eg, api=api)
+def publish_workflow_view():
+ """responds with the form for the example"""
+ example = get_example_by_number(session["manifest"], example_number, api)
+ additional_page_data = next((p for p in example["AdditionalPage"] if p["Name"] == "publish_workflow"), None)
+
+ args = {
+ "account_id": session["ds_account_id"],
+ "base_path": DS_CONFIG["maestro_api_client_host"],
+ "access_token": session["ds_access_token"]
+ }
+ try:
+ consent_url = publish_workflow(args, session["workflow_id"])
+
+ if consent_url:
+ return render_template(
+ "maestro/eg001_publish_workflow.html",
+ title=example["ExampleName"],
+ message=additional_page_data["ResultsPageText"],
+ consent_url=consent_url
+ )
+
+ except ApiException as err:
+ if hasattr(err, "status"):
+ if err.status == 403:
+ return render_template(
+ "error.html",
+ err=err,
+ error_code=err.status,
+ error_message=session["manifest"]["SupportingTexts"]["ContactSupportToEnableFeature"]
+ .format("Maestro")
+ )
+
+ return process_error(err)
+
+ return render_template(
+ "maestro/eg001_trigger_workflow.html",
+ title=example["ExampleName"],
+ example=example,
+ template_ok=True,
+ source_file="eg001_trigger_workflow.py",
+ source_url=DS_CONFIG["github_example_url"] + "eg001_trigger_workflow.py",
+ documentation=DS_CONFIG["documentation"] + eg,
+ show_doc=DS_CONFIG["documentation"],
+ )
diff --git a/app/maestro/views/eg002_cancel_workflow.py b/app/maestro/views/eg002_cancel_workflow.py
new file mode 100644
index 0000000..e5a86fa
--- /dev/null
+++ b/app/maestro/views/eg002_cancel_workflow.py
@@ -0,0 +1,103 @@
+"""Example 002: How to cancel a Maestro workflow instance"""
+
+import json
+
+from docusign_maestro.client.api_exception import ApiException
+from flask import render_template, Blueprint, session
+
+from ..examples.eg002_cancel_workflow import Eg002CancelWorkflowController
+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 = 2
+api = API_TYPE["MAESTRO"]
+eg = f"mseg00{example_number}" # reference (and url) for this example
+mseg002 = Blueprint(eg, __name__)
+
+
+@mseg002.route(f"/{eg}", methods=["POST"])
+@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"])
+@authenticate(eg=eg, api=api)
+def cancel_workflow():
+ """
+ 1. Get required arguments
+ 2. Call the worker method
+ 3. Show results
+ """
+ example = get_example_by_number(session["manifest"], example_number, api)
+
+ # 1. Get required arguments
+ args = Eg002CancelWorkflowController.get_args()
+ try:
+ # 1. Call the worker method
+ results = Eg002CancelWorkflowController.cancel_workflow_instance(args)
+ except ApiException as err:
+ if hasattr(err, "status"):
+ if err.status == 403:
+ return render_template(
+ "error.html",
+ err=err,
+ error_code=err.status,
+ error_message=session["manifest"]["SupportingTexts"]["ContactSupportToEnableFeature"]
+ .format("Maestro")
+ )
+
+ return process_error(err)
+ # 3. Show results
+ return render_template(
+ "example_done.html",
+ title=example["ExampleName"],
+ message=example["ResultsPageText"].format(session["instance_id"]),
+ json=json.dumps(json.dumps(results.to_dict()))
+ )
+
+
+@mseg002.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)
+
+ instance_ok = False
+ workflow_id = session.get("workflow_id", None)
+ instance_id = session.get("instance_id", None)
+ if workflow_id and instance_id:
+ args = {
+ "account_id": session["ds_account_id"],
+ "base_path": DS_CONFIG["maestro_api_client_host"],
+ "access_token": session["ds_access_token"],
+ "workflow_id": workflow_id,
+ "instance_id": instance_id
+ }
+
+ try:
+ state = Eg002CancelWorkflowController.get_instance_state(args)
+ instance_ok = state.lower() == "in progress"
+ except ApiException as err:
+ if hasattr(err, "status"):
+ if err.status == 403:
+ return render_template(
+ "error.html",
+ err=err,
+ error_code=err.status,
+ error_message=session["manifest"]["SupportingTexts"]["ContactSupportToEnableFeature"]
+ .format("Maestro")
+ )
+
+ return process_error(err)
+
+ return render_template(
+ "maestro/eg002_cancel_workflow.html",
+ title=example["ExampleName"],
+ example=example,
+ instance_ok=instance_ok,
+ workflow_id=workflow_id,
+ instance_id=instance_id,
+ source_file="eg002_cancel_workflow.py",
+ source_url=DS_CONFIG["github_example_url"] + "eg002_cancel_workflow.py",
+ documentation=DS_CONFIG["documentation"] + eg,
+ show_doc=DS_CONFIG["documentation"],
+ )
diff --git a/app/maestro/views/eg003_get_workflow_status.py b/app/maestro/views/eg003_get_workflow_status.py
new file mode 100644
index 0000000..6a5cc37
--- /dev/null
+++ b/app/maestro/views/eg003_get_workflow_status.py
@@ -0,0 +1,76 @@
+"""Example 003: How to get the status of a Maestro workflow instance"""
+
+import json
+
+from docusign_maestro.client.api_exception import ApiException
+from flask import render_template, Blueprint, session
+
+from ..examples.eg003_get_workflow_status import Eg003GetWorkflowStatusController
+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["MAESTRO"]
+eg = f"mseg00{example_number}" # reference (and url) for this example
+mseg003 = Blueprint(eg, __name__)
+
+
+@mseg003.route(f"/{eg}", methods=["POST"])
+@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"])
+@authenticate(eg=eg, api=api)
+def get_workflow_status():
+ """
+ 1. Get required arguments
+ 2. Call the worker method
+ 3. Show results
+ """
+ example = get_example_by_number(session["manifest"], example_number, api)
+
+ # 1. Get required arguments
+ args = Eg003GetWorkflowStatusController.get_args()
+ try:
+ # 1. Call the worker method
+ results = Eg003GetWorkflowStatusController.get_workflow_instance(args)
+ except ApiException as err:
+ if hasattr(err, "status"):
+ if err.status == 403:
+ return render_template(
+ "error.html",
+ err=err,
+ error_code=err.status,
+ error_message=session["manifest"]["SupportingTexts"]["ContactSupportToEnableFeature"]
+ .format("Maestro")
+ )
+
+ return process_error(err)
+ # 3. Show results
+ return render_template(
+ "example_done.html",
+ title=example["ExampleName"],
+ message=example["ResultsPageText"].format(results.instance_state),
+ json=json.dumps(json.dumps(results.to_dict(), default=str))
+ )
+
+
+@mseg003.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)
+
+ workflow_id = session.get("workflow_id", None)
+ instance_id = session.get("instance_id", None)
+ return render_template(
+ "maestro/eg003_get_workflow_status.html",
+ title=example["ExampleName"],
+ example=example,
+ workflow_id=workflow_id,
+ instance_id=instance_id,
+ source_file="eg003_get_workflow_status.py",
+ source_url=DS_CONFIG["github_example_url"] + "eg003_get_workflow_status.py",
+ documentation=DS_CONFIG["documentation"] + eg,
+ show_doc=DS_CONFIG["documentation"],
+ )
diff --git a/app/static/assets/search.js b/app/static/assets/search.js
index 92e2e2c..8c564c7 100644
--- a/app/static/assets/search.js
+++ b/app/static/assets/search.js
@@ -6,6 +6,7 @@ const DS_SEARCH = (function () {
ROOMS: "rooms",
ADMIN: "admin",
CONNECT: "connect",
+ MAESTRO: "maestro",
WEBFORMS: "webforms"
}
@@ -143,6 +144,8 @@ const DS_SEARCH = (function () {
return "eg";
case API_TYPES.CONNECT:
return "cneg";
+ case API_TYPES.MAESTRO:
+ return "mseg";
case API_TYPES.WEBFORMS:
return "weg";
}
diff --git a/app/templates/error.html b/app/templates/error.html
index 35b9a86..1a8819d 100644
--- a/app/templates/error.html
+++ b/app/templates/error.html
@@ -7,7 +7,7 @@ Problem: an error occurred
Error information:
{% if error_code %}
- {{ error_code }}: {{ error_message }}
+ {{ error_code }}: {{ error_message | safe }}
{% else %}
{{ err }}
{% endif %}
diff --git a/app/templates/home.html b/app/templates/home.html
index 72e9b04..701c56f 100644
--- a/app/templates/home.html
+++ b/app/templates/home.html
@@ -45,7 +45,7 @@ {{ group["Name"] }}
{% for example in group["Examples"] -%}
{% if not example["SkipForLanguages"] or "python" not in example["SkipForLanguages"] %}
{% if example.CFREnabled != "CFROnly" %}
- {% set api_prefix = "a" if api["Name"] == "Admin" else "c" if api["Name"] == "Click" else "r" if api["Name"] == "Rooms" else "m" if api["Name"] == "Monitor" else "cn" if api["Name"] == "Connect" else "w" if api["Name"] == "WebForms" else "" %}
+ {% set api_prefix = "a" if api["Name"] == "Admin" else "c" if api["Name"] == "Click" else "r" if api["Name"] == "Rooms" else "m" if api["Name"] == "Monitor" else "cn" if api["Name"] == "Connect" else "ms" if api["Name"] == "Maestro" else "w" if api["Name"] == "WebForms" else "" %}
diff --git a/app/templates/maestro/eg001_publish_workflow.html b/app/templates/maestro/eg001_publish_workflow.html
new file mode 100644
index 0000000..b17ae2d
--- /dev/null
+++ b/app/templates/maestro/eg001_publish_workflow.html
@@ -0,0 +1,11 @@
+ {% extends "base.html" %} {% block content %}
+
+{{ title }}
+
{{ message.format(consent_url) | safe }}
+
+
+
+{% endblock %}
diff --git a/app/templates/maestro/eg001_trigger_workflow.html b/app/templates/maestro/eg001_trigger_workflow.html
new file mode 100644
index 0000000..3660b61
--- /dev/null
+++ b/app/templates/maestro/eg001_trigger_workflow.html
@@ -0,0 +1,59 @@
+ {% extends "base.html" %} {% block content %}
+
+{% include 'example_info.html' %}
+
+{% set form_index = 0 %}
+{% set instance_name_index = 0 %}
+{% set signer_email_index = 1 %}
+{% set signer_name_index = 2 %}
+{% set cc_email_index = 3 %}
+{% set cc_name_index = 4 %}
+{% set redirect_to8_index = 0 %}
+
+{% if template_ok %}
+
+{% else %}
+ {{ example['RedirectsToOtherCodeExamples'][redirect_to8_index]['RedirectText'].format('href="eg008"') | safe }}
+
+
+{% endif %}
+
+{% endblock %}
diff --git a/app/templates/maestro/eg002_cancel_workflow.html b/app/templates/maestro/eg002_cancel_workflow.html
new file mode 100644
index 0000000..a0545d7
--- /dev/null
+++ b/app/templates/maestro/eg002_cancel_workflow.html
@@ -0,0 +1,38 @@
+ {% extends "base.html" %} {% block content %}
+
+{% include 'example_info.html' %}
+
+{% set form_index = 0 %}
+{% set workflow_id_index = 0 %}
+{% set instance_id_index = 1 %}
+{% set redirect_to8_index = 0 %}
+
+{% if instance_ok %}
+
+{% else %}
+ {{ example['RedirectsToOtherCodeExamples'][redirect_to8_index]['RedirectText'].format('href="mseg001"') | safe }}
+
+
+{% endif %}
+
+{% endblock %}
diff --git a/app/templates/maestro/eg003_get_workflow_status.html b/app/templates/maestro/eg003_get_workflow_status.html
new file mode 100644
index 0000000..50b4abd
--- /dev/null
+++ b/app/templates/maestro/eg003_get_workflow_status.html
@@ -0,0 +1,38 @@
+ {% extends "base.html" %} {% block content %}
+
+{% include 'example_info.html' %}
+
+{% set form_index = 0 %}
+{% set workflow_id_index = 0 %}
+{% set instance_id_index = 1 %}
+{% set redirect_to8_index = 0 %}
+
+{% if workflow_id and instance_id %}
+
+{% else %}
+ {{ example['RedirectsToOtherCodeExamples'][redirect_to8_index]['RedirectText'].format('href="mseg001"') | safe }}
+
+
+{% endif %}
+
+{% endblock %}