<jupyter_start><jupyter_text>Set up Open Banking Account Consent APIsRunning this script will create the APIs that we will be monitoring for accounts consents. They do notrequire user authentication. They are also used for the user auth flow.The API setups are defined with tags that allow us to easily provide OBIE metric reporting. Requirements1. Create an APImetrics Project1. Get an API key with EDITOR permissions for the project, enter it below1. Get the maTLS transport certificate and key in PEM format, enter paths below1. Run this workbook to create all APIs and Workflow1. Create an environment variable "financial_id" - TBD - add this hereNB: Until we publish a Conditions API, you will need to add conditions to the API to extract variables<jupyter_code># Enter intended project's APImetrics API key here:
print("Enter your APImetrics API key for the intended project")
API_KEY = input()
# Enter the bank's full path, including https and without trailing /
BANK_URL = input("Enter the bank's full path including https and up to aisp without trailing / : ")

SSL_KEY_PATH = "" #"./local/TPP_OB_Transport.key"
SSL_CERT_PATH = "" #"./local/TTP_OB_Transport.pem"

OAUTH_METHOD = input("Enter OAuth method: ") or "tls_client_auth"
# if OAUTH_METHOD == "client_secret_post" or OAUTH_METHOD == "client_secret_basic":
# # CLIENT_ID = '...'
# # CLIENT_SECRET = '...'

# The OBIE version ID we're working against
VERSION = "3.1"

from urllib.parse import urlparse

url_info = urlparse(BANK_URL)
DOMAIN, _, _ = url_info.netloc.partition(':') # removes the port 
# Helper functions
import requests
import json
from apimetrics_api import APImetricsAPI
    
# An instance of the class that calls the APImetrics API
CLIENT = APImetricsAPI(API_KEY)

# [CLIENT.delete_token(o['id']) for o in CLIENT.tokens.values()]
# [CLIENT.delete_auth(o['id']) for o in CLIENT.auths.values()]
# [CLIENT.delete_call(o['id']) for o in CLIENT.calls.values()]
# [CLIENT.delete_workflow(o['id']) for o in CLIENT.workflows.values()]<jupyter_output><empty_output><jupyter_text>Create Auth Setting and Token for Bank<jupyter_code># First create Auth Setting
tag = 'auth:bank_matls'
if tag not in CLIENT.auths_by_tag:

    ssl_key = None
    ssl_cert = None

    if SSL_KEY_PATH:
        with open(SSL_KEY_PATH) as stream:
            ssl_key = stream.read()

    if SSL_CERT_PATH:
        with open(SSL_CERT_PATH) as stream:
            ssl_cert = stream.read()
    
    setup = {
        "access": {
            "keys": False,
            "org_keys": False,
            "org_settings": True,
            "settings": False,
        },
        "keys": {},
        "meta": {
            "domain": DOMAIN,
            "documentation": {"keys": "", "docs": "", "apps": "", "provider": ""},
            "name": "Transport MATLS",
            "tags": [tag],
            "description": "Mutual Authenticated TLS for calls to bank APIs",
        },
        "settings": {
            "auth_type": "MANUAL",
            "ssl_key": ssl_key,
            "ssl_cert": ssl_cert,
        },
    }
    if OAUTH_METHOD == "client_secret_post" or OAUTH_METHOD == "client_secret_basic":
        setup["keys"]["client_id"] = CLIENT_ID
        setup["keys"]["client_secret"] = CLIENT_SECRET
    
    auth = CLIENT.create_auth(setup)
    print(f"Created Auth Setting {auth['meta']['name']} with id {auth['id']}")
    
# Second Create Token
if CLIENT.auths_by_tag['auth:bank_matls'] not in CLIENT.tokens_by_auth:
    setup = {
        'meta': {
            'name': 'Authenticated User Access Token',
            'domain': DOMAIN,
            'auth_id': CLIENT.auths_by_tag['auth:bank_matls']
        },
        'token': {}
    }
    token = CLIENT.create_token(setup)
    print(f"Created Auth Token {token['meta']['name']} with id {token['id']}")<jupyter_output><empty_output><jupyter_text>Create APIs<jupyter_code># Account Access Consent Create (minimal)
tag = f"banks:{VERSION}:account-access-consents:create_min"
if tag not in CLIENT.calls_by_tag:
    body = {"Data": {"Permissions": ["ReadAccountsBasic"]}, "Risk": {}}
    body_str = json.dumps(body, indent=2)
    setup = {
        "meta": {
            "description": None,
            "tags": ["api_type:create", "sector:financial", "ob_id:1", f"ob_v:{VERSION}"]
            + [tag],
            "name": f"v{VERSION}: Account-Access-Consents: Create (minimal)",
            "workspace": "global",
        },
        "request": {
            "body": body_str,
            "parameters": [],
            "url": "{}/account-access-consents".format(BANK_URL),
            "auth_id": CLIENT.auths_by_tag['auth:bank_matls'],
            "headers": [
                {"value": "application/json", "key": "Accept"},
                {"value": "application/json", "key": "Content-Type"},
                {"value": "Bearer %%ACCESS_TOKEN%%", "key": "Authorization"},
                {"value": "{{financial_id}}", "key": "x-fapi-financial-id"},
                {"value": "%%GUID%%", "key": "x-fapi-interaction-id"},
                {
                    "value": "APIMetrics-%%TEST_RUN_RESULT_ID%%",
                    "key": "x-idempotency-key",
                },
            ],
            "token_id": None,
            "method": "POST",
        },
    }
    data = CLIENT.create_call(setup)
    print(f"Created API {data['meta']['name']} with id {data['id']}")
    
    id_str = data['id']
    conditions = {
        "conditions": [
            {
              "source": "RESPONSE_BODY", 
              "test_result_on_true": None, 
              "val": "", 
              "variable_path": "Data.ConsentId", 
              "variable_name": "CONSENT_ID", 
              "test_result_on_false": "CONTENT_ERROR", 
              "condition": "EXISTS"
            },
          ]
    }
    CLIENT.set_call_conditions(id_str, conditions)
# Account Access Consent Create (maximal)
tag = f"banks:{VERSION}:account-access-consents:create_max"
if tag not in CLIENT.calls_by_tag:
    body = {
        "Data": {
            "Permissions": [
                "ReadAccountsDetail",
                "ReadBalances",
                "ReadBeneficiariesDetail",
                "ReadDirectDebits",
                "ReadOffers",
                "ReadParty",
                "ReadPartyPSU",
                "ReadProducts",
                "ReadStandingOrdersDetail",
                "ReadScheduledPaymentsDetail",
                "ReadTransactionsCredits",
                "ReadTransactionsDebits",
                "ReadTransactionsDetail",
                "ReadStatementsDetail",
            ]
        },
        "Risk": {},
    }
    body_str = json.dumps(body, indent=2)
    setup = {
        "meta": {
            "description": None,
            "tags": ["api_type:create", "sector:financial", "ob_id:1", f"ob_v:{VERSION}"]
            + [tag],
            "name": f"v{VERSION}: Account-Access-Consents: Create (maximal)",
            "workspace": "global",
        },
        "request": {
            "body": body_str,
            "parameters": [],
            "url": "{}/account-access-consents".format(BANK_URL),
            "auth_id": CLIENT.auths_by_tag['auth:bank_matls'],
            "headers": [
                {"value": "application/json", "key": "Accept"},
                {"value": "application/json", "key": "Content-Type"},
                {"value": "Bearer %%ACCESS_TOKEN%%", "key": "Authorization"},
                {"value": "{{financial_id}}", "key": "x-fapi-financial-id"},
                {"value": "%%GUID%%", "key": "x-fapi-interaction-id"},
                {
                    "value": "APIMetrics-%%TEST_RUN_RESULT_ID%%",
                    "key": "x-idempotency-key",
                },
            ],
            "token_id": None,
            "method": "POST",
        },
    }
    data = CLIENT.create_call(setup)
    print(f"Created API {data['meta']['name']} with id {data['id']}")
    
    id_str = data['id']
    conditions = {
        "conditions": [
            {
              "source": "RESPONSE_BODY", 
              "test_result_on_true": None, 
              "val": "", 
              "variable_path": "Data.ConsentId", 
              "variable_name": "CONSENT_ID", 
              "test_result_on_false": "CONTENT_ERROR", 
              "condition": "EXISTS"
            },
          ]
    }
    CLIENT.set_call_conditions(id_str, conditions)
# Account Access Consent Read
tag = f"banks:{VERSION}:account-access-consents:read"
if tag not in CLIENT.calls_by_tag:
    setup = {
        "meta": {
            "description": None,
            "tags": ["api_type:read", "sector:financial", "ob_id:2", f"ob_v:{VERSION}"]
            + [tag],
            "name": "v3.1: Account-Access-Consents: Get",
            "workspace": "global",
        },
        "request": {
            "body": None,
            "parameters": [],
            "url": "{}/account-access-consents/__CONSENT_ID__".format(BANK_URL),
            "auth_id": CLIENT.auths_by_tag['auth:bank_matls'],
            "headers": [
                {"value": "application/json", "key": "Accept"},
                {"value": "application/json", "key": "Content-Type"},
                {"value": "Bearer %%ACCESS_TOKEN%%", "key": "Authorization"},
                {"value": "{{financial_id}}", "key": "x-fapi-financial-id"},
                {"value": "%%GUID%%", "key": "x-fapi-interaction-id"},
                {
                    "value": "APIMetrics-%%TEST_RUN_RESULT_ID%%",
                    "key": "x-idempotency-key",
                },
            ],
            "token_id": None,
            "method": "GET",
        },
    }
    data = CLIENT.create_call(setup)
    print(f"Created API {data['meta']['name']} with id {data['id']}")
# Account Access Consent Delete
tag = f"banks:{VERSION}:account-access-consents:delete"
if tag not in CLIENT.calls_by_tag:
    setup = {
        "meta": {
            "description": None,
            "tags": ["api_type:delete", "sector:financial", "ob_id:3", "ob_v:3.1", tag],
            "name": "v3.1: Account-Access-Consents: Delete",
            "workspace": "global",
        },
        "request": {
            "body": None,
            "parameters": [],
            "url": "{}/account-access-consents/__CONSENT_ID__".format(BANK_URL),
            "auth_id": CLIENT.auths_by_tag['auth:bank_matls'],
            "headers": [
                {"value": "Bearer %%ACCESS_TOKEN%%", "key": "Authorization"},
                {"value": "{{financial_id}}", "key": "x-fapi-financial-id"},
                {"value": "%%GUID%%", "key": "x-fapi-interaction-id"},
                {
                    "value": "APIMetrics-%%TEST_RUN_RESULT_ID%%",
                    "key": "x-idempotency-key",
                },
            ],
            "token_id": None,
            "method": "DELETE",
        },
    }
    data = CLIENT.create_call(setup)
    print(f"Created API {data['meta']['name']} with id {data['id']}")<jupyter_output><empty_output><jupyter_text>Create Workflow to exercise Lifecycle<jupyter_code>tag = f'banks:{VERSION}:account-access-consents:lifecycle'
if tag not in CLIENT.workflows_by_tag:
    call_tags = [
        'banks:oauth:client_credentials',
        f'banks:{VERSION}:account-access-consents:create_min',
        f'banks:{VERSION}:account-access-consents:read',
        f'banks:{VERSION}:account-access-consents:delete',
    ]
    
    if OAUTH_METHOD == "private_key_jwt":
        call_tags = [
            'jwt:sign:client_credentials',  # only if we're using private_key_jwt auth
        ] + call_tags
    
    for t in call_tags:
        assert t in CLIENT.calls_by_tag, f"API {t} does not exist"

    setup = {
      "meta": {
        "name": f"{VERSION}: Account-Access-Consents: Lifecycle", 
        "workspace": "global", 
        "tags": [tag], 
      }, 
      "workflow": {
        "handle_cookies": False,
        "stop_on_failure": True,
        "call_ids": [CLIENT.calls_by_tag[t] for t in call_tags]
      }
    }
    workflow = CLIENT.create_workflow(setup)
    print(f"Created Workflow {workflow['meta']['name']} with id {workflow['id']}")<jupyter_output><empty_output><jupyter_text>Set Env Variable<jupyter_code>CLIENT.set_env_variable('global', 'financial_id', input("Enter financial ID: "))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15257412838058, 'fraction_numerical': 0.0015477354187031606, 'mean_word_length': 2.286134903640257, 'pattern_counts': {'":': 149, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '91692', 'license': 'Apache-2.0', 'n_tokens_mistral': 3922, 'n_tokens_neox': 3804, 'n_words': 796, 'path': 'OBIE - Account Consent APIs.ipynb', 'repo_name': 'APImetrics/openid-scripts', 'size': '12276'}