Skip to content

Instantly share code, notes, and snippets.

@mroy-seedbox
Last active March 25, 2024 19:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mroy-seedbox/336edfe6bab22f3773e0feee819d04de to your computer and use it in GitHub Desktop.
Save mroy-seedbox/336edfe6bab22f3773e0feee819d04de to your computer and use it in GitHub Desktop.
Tableau API Token Expiration Issue
import os
import sys
import json
import time
from datetime import datetime
from urllib import request
DELAY_SECONDS = int(sys.argv[1]) if len(sys.argv) > 1 else 60
PING_COUNT = int(sys.argv[2]) if len(sys.argv) > 2 else 1
PING_DELAY_SECONDS = int(sys.argv[3]) if len(sys.argv) > 3 else 1
PAT2_DELAY = int(sys.argv[4]) if len(sys.argv) > 4 else -1
print(f"Checking token validity every {DELAY_SECONDS} seconds (sending {PING_COUNT} pings in {PING_DELAY_SECONDS} second intervals)")
if PAT2_DELAY >= 0: print(f"Second PAT will be activated {PAT2_DELAY} minutes after the first PAT is signed in.")
# Adjust the delay to account for the pings
DELAY_SECONDS = DELAY_SECONDS - (PING_COUNT * PING_DELAY_SECONDS)
host = "https://us-east-1.online.tableau.com/api/3.22"
site = os.environ["TABLEAU_SITE"]
pat1 = {
"name": os.environ.get("TABLEAU_PAT_1_NAME", "test_pat1"),
"token": os.environ["TABLEAU_PAT_1"],
}
pat2 = {
"name": os.environ.get("TABLEAU_PAT2__NAME", "test_pat2"),
"token": os.environ.get("TABLEAU_PAT_2"),
}
def signin(pat):
req = request.Request(
host + "/auth/signin",
method="POST",
data=json.dumps({
"credentials": {
"personalAccessTokenName": pat["name"],
"personalAccessTokenSecret": pat["token"],
"site": {
"contentUrl": site
}
}
}).encode(),
)
req.add_header("Accept", "application/json")
req.add_header("Content-Type", "application/json; charset=utf-8")
resp = request.urlopen(req)
data = json.loads(resp.read())["credentials"]
data["ts"] = datetime.now()
return data
def check_token(creds, count=1):
req = request.Request(f"{host}/sites/{creds["site"]["id"]}/projects")
req.add_header("X-Tableau-Auth", creds["token"])
try:
resp = request.urlopen(req)
if resp.status > 200:
return False
if count > 1:
time.sleep(PING_DELAY_SECONDS)
return check_token(creds, count - 1)
return True
except Exception as e:
return False
def get_seconds_since(dt):
return round((datetime.now() - dt).total_seconds() / 60, 2)
pat1_creds = signin(pat1)
pat2_creds = None
while True:
pat1_valid = check_token(pat1_creds, PING_COUNT)
minutes_since_pat1 = get_seconds_since(pat1_creds["ts"])
if PAT2_DELAY >= 0 and pat2_creds == None and minutes_since_pat1 > PAT2_DELAY:
pat2_creds = signin(pat2)
if pat2_creds:
pat2_valid = check_token(pat2_creds, PING_COUNT)
minutes_since_pat2 = get_seconds_since(pat2_creds["ts"])
if pat2_valid:
print(f"{datetime.now()}: Credentials2 still valid after {minutes_since_pat2} minutes")
else:
print(f"{datetime.now()}: Credentials2 no longer valid after {minutes_since_pat2} minutes")
pat2_creds = None
if pat1_valid:
print(f"{datetime.now()}: Credentials1 still valid after {minutes_since_pat1} minutes")
time.sleep(DELAY_SECONDS)
else:
print(f"{datetime.now()}: Credentials1 no longer valid after {minutes_since_pat1} minutes")
pat1_creds = signin(pat1)
@mroy-seedbox
Copy link
Author

mroy-seedbox commented Mar 20, 2024

Usage:

> export TABLEAU_SITE=""
> export TABLEAU_PAT_1_NAME=""
> export TABLEAU_PAT_1=""

> python3 tableau-api-token-test.py 300
Checking token validity every 300 seconds (sending 1 pings in 1 second intervals)
2024-03-20 12:01:20.724918: Credentials1 still valid after 0.02 minutes
2024-03-20 12:06:21.031738: Credentials1 still valid after 5.03 minutes
2024-03-20 12:11:23.023102: Credentials1 still valid after 10.06 minutes
2024-03-20 12:16:23.491478: Credentials1 still valid after 15.07 minutes
2024-03-20 12:21:23.926130: Credentials1 still valid after 20.07 minutes
2024-03-20 12:26:24.174169: Credentials1 still valid after 25.08 minutes
2024-03-20 12:31:24.811563: Credentials1 no longer valid after 30.09 minutes
2024-03-20 12:31:26.235623: Credentials1 still valid after 0.02 minutes
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment