Last active
March 25, 2024 19:25
-
-
Save mroy-seedbox/336edfe6bab22f3773e0feee819d04de to your computer and use it in GitHub Desktop.
Tableau API Token Expiration Issue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage: