adobe-to-docusign-migrator/src/adobe_api.py

146 lines
5.2 KiB
Python

import os
import sys
import requests
from dotenv import load_dotenv, set_key
load_dotenv()
sys.path.insert(0, os.path.dirname(__file__))
from utils.retry import RetryableHTTPError, raise_for_retryable_status, retry_with_backoff
_RETRY = dict(max_retries=3, base_delay=1.0, max_delay=16.0, retryable_exceptions=(RetryableHTTPError,))
SHARD = "eu2"
TOKEN_URL = f"https://api.{SHARD}.adobesign.com/oauth/v2/token" # initial auth code exchange
REFRESH_URL = f"https://api.{SHARD}.adobesign.com/oauth/v2/refresh" # token refresh (non-standard separate endpoint)
ENV_FILE = os.path.join(os.path.dirname(__file__), "..", ".env")
def _refresh_access_token():
client_id = os.getenv("ADOBE_CLIENT_ID")
client_secret = os.getenv("ADOBE_CLIENT_SECRET")
refresh_token = os.getenv("ADOBE_REFRESH_TOKEN")
if not all([client_id, client_secret, refresh_token]):
raise RuntimeError("Missing credentials for token refresh. Run src/adobe_auth.py first.")
data = {
"grant_type": "refresh_token",
"refresh_token": refresh_token,
"client_id": client_id,
"client_secret": client_secret,
}
resp = requests.post(REFRESH_URL, data=data)
if not resp.ok:
raise RuntimeError(
f"Adobe Sign refresh token is invalid or expired ({resp.status_code}: {resp.text}). "
"Run `python3 src/adobe_auth.py` to re-authenticate."
)
new_token = resp.json()["access_token"]
set_key(os.path.abspath(ENV_FILE), "ADOBE_ACCESS_TOKEN", new_token)
os.environ["ADOBE_ACCESS_TOKEN"] = new_token
return new_token
@retry_with_backoff(**_RETRY)
def adobe_api_post_multipart(endpoint, files, data=None):
"""Upload a file via multipart/form-data (e.g. transient documents)."""
token = os.getenv("ADOBE_ACCESS_TOKEN")
base_url = os.getenv("ADOBE_SIGN_BASE_URL", f"https://api.{SHARD}.adobesign.com/api/rest/v6")
headers = {"Authorization": f"Bearer {token}"}
url = f"{base_url}/{endpoint}"
resp = requests.post(url, headers=headers, files=files, data=data or {})
if resp.status_code == 401:
token = _refresh_access_token()
headers["Authorization"] = f"Bearer {token}"
resp = requests.post(url, headers=headers, files=files, data=data or {})
raise_for_retryable_status(resp)
return resp.json()
@retry_with_backoff(**_RETRY)
def adobe_api_post_json(endpoint, body):
"""POST JSON body to an Adobe Sign endpoint."""
token = os.getenv("ADOBE_ACCESS_TOKEN")
base_url = os.getenv("ADOBE_SIGN_BASE_URL", f"https://api.{SHARD}.adobesign.com/api/rest/v6")
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
url = f"{base_url}/{endpoint}"
resp = requests.post(url, headers=headers, json=body)
if resp.status_code == 401:
token = _refresh_access_token()
headers["Authorization"] = f"Bearer {token}"
resp = requests.post(url, headers=headers, json=body)
raise_for_retryable_status(resp)
return resp.json()
@retry_with_backoff(**_RETRY)
def adobe_api_put_json(endpoint, body):
"""PUT JSON body to an Adobe Sign endpoint."""
token = os.getenv("ADOBE_ACCESS_TOKEN")
base_url = os.getenv("ADOBE_SIGN_BASE_URL", f"https://api.{SHARD}.adobesign.com/api/rest/v6")
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
url = f"{base_url}/{endpoint}"
resp = requests.put(url, headers=headers, json=body)
if resp.status_code == 401:
token = _refresh_access_token()
headers["Authorization"] = f"Bearer {token}"
resp = requests.put(url, headers=headers, json=body)
raise_for_retryable_status(resp)
return resp.json()
@retry_with_backoff(**_RETRY)
def adobe_api_get_bytes(endpoint):
"""Download binary content (e.g. PDF files) from the Adobe Sign API."""
token = os.getenv("ADOBE_ACCESS_TOKEN")
base_url = os.getenv("ADOBE_SIGN_BASE_URL", f"https://api.{SHARD}.adobesign.com/api/rest/v6")
headers = {"Authorization": f"Bearer {token}"}
url = f"{base_url}/{endpoint}"
resp = requests.get(url, headers=headers)
if resp.status_code == 401:
token = _refresh_access_token()
headers["Authorization"] = f"Bearer {token}"
resp = requests.get(url, headers=headers)
raise_for_retryable_status(resp)
return resp.content
@retry_with_backoff(**_RETRY)
def adobe_api_get(endpoint, params=None):
token = os.getenv("ADOBE_ACCESS_TOKEN")
base_url = os.getenv("ADOBE_SIGN_BASE_URL", f"https://api.{SHARD}.adobesign.com/api/rest/v6")
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
}
url = f"{base_url}/{endpoint}"
resp = requests.get(url, headers=headers, params=params)
if resp.status_code == 401:
# Token expired — refresh and retry once
token = _refresh_access_token()
headers["Authorization"] = f"Bearer {token}"
resp = requests.get(url, headers=headers, params=params)
raise_for_retryable_status(resp)
return resp.json()
if __name__ == "__main__":
library_docs = adobe_api_get("libraryDocuments")
print("Library Documents:", library_docs)