Show connected account names in UI
This commit is contained in:
parent
0aba091d56
commit
aaa72be54e
|
|
@ -74,7 +74,13 @@ def test_adobe_connect_env_stores_token(monkeypatch):
|
|||
monkeypatch.setenv("ADOBE_REFRESH_TOKEN", "existing-refresh")
|
||||
|
||||
from unittest.mock import patch
|
||||
with patch("adobe_api._refresh_access_token", return_value="refreshed-token"):
|
||||
with patch("adobe_api._refresh_access_token", return_value="refreshed-token"), \
|
||||
patch("web.routers.auth._fetch_adobe_profile", return_value={
|
||||
"adobe_user_name": "Paul Adobe",
|
||||
"adobe_user_email": "paul@example.com",
|
||||
"adobe_account_name": "Paul Sandbox",
|
||||
"adobe_account_id": "adobe-account-123",
|
||||
}):
|
||||
resp = client.get("/api/auth/adobe/connect")
|
||||
|
||||
assert resp.status_code == 200
|
||||
|
|
@ -82,6 +88,9 @@ def test_adobe_connect_env_stores_token(monkeypatch):
|
|||
session_cookie = resp.cookies.get("migrator_session")
|
||||
status_resp = client.get("/api/auth/status", cookies={"migrator_session": session_cookie})
|
||||
assert status_resp.json()["adobe"] is True
|
||||
assert status_resp.json()["adobe_account_name"] == "Paul Sandbox"
|
||||
assert status_resp.json()["adobe_account_id"] == "adobe-account-123"
|
||||
assert status_resp.json()["adobe_label"] == "Paul Sandbox"
|
||||
|
||||
|
||||
def test_adobe_connect_env_fails_without_credentials(monkeypatch):
|
||||
|
|
|
|||
|
|
@ -40,6 +40,59 @@ _ADOBE_AUTH_URL = "https://secure.eu2.adobesign.com/public/oauth/v2"
|
|||
_ADOBE_TOKEN_URL = "https://api.eu2.adobesign.com/oauth/v2/token"
|
||||
|
||||
|
||||
async def _fetch_adobe_profile(access_token: str) -> dict:
|
||||
"""
|
||||
Best-effort Adobe Sign profile lookup used only for nicer UI labels.
|
||||
This should never block a successful connection if Adobe returns sparse data.
|
||||
"""
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
resp = await client.get(
|
||||
f"{settings.adobe_sign_base_url}/users/me",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
)
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
if not resp.is_success:
|
||||
return {}
|
||||
|
||||
data = resp.json() if resp.content else {}
|
||||
if not isinstance(data, dict):
|
||||
return {}
|
||||
|
||||
company = data.get("company") or {}
|
||||
account_name = (
|
||||
company.get("name")
|
||||
or data.get("companyName")
|
||||
or data.get("accountName")
|
||||
or data.get("name")
|
||||
)
|
||||
account_id = (
|
||||
company.get("id")
|
||||
or data.get("companyId")
|
||||
or data.get("accountId")
|
||||
)
|
||||
|
||||
return {
|
||||
"adobe_user_name": data.get("name"),
|
||||
"adobe_user_email": data.get("email"),
|
||||
"adobe_account_name": account_name,
|
||||
"adobe_account_id": account_id,
|
||||
}
|
||||
|
||||
|
||||
async def _merge_adobe_profile(session: dict, access_token: str) -> dict:
|
||||
profile = await _fetch_adobe_profile(access_token)
|
||||
if not profile:
|
||||
return session
|
||||
updated = dict(session)
|
||||
for key, value in profile.items():
|
||||
if value:
|
||||
updated[key] = value
|
||||
return updated
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Status
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -55,8 +108,10 @@ def auth_status(request: Request):
|
|||
docusign_account = None
|
||||
return {
|
||||
**session_public_view(session),
|
||||
"adobe_label": "Adobe Sign",
|
||||
"adobe_label": session.get("adobe_account_name") or session.get("adobe_user_name") or "Adobe Sign",
|
||||
"docusign_label": session.get("docusign_user_name") or "Docusign",
|
||||
"adobe_account_name": session.get("adobe_account_name"),
|
||||
"adobe_account_id": session.get("adobe_account_id"),
|
||||
"docusign_account_id": (docusign_account or {}).get("account_id"),
|
||||
"docusign_account_name": (docusign_account or {}).get("account_name"),
|
||||
"base_url": (docusign_account or {}).get("base_url", settings.docusign_base_url),
|
||||
|
|
@ -135,6 +190,7 @@ async def adobe_exchange(body: AdobeExchangeRequest, request: Request):
|
|||
session["adobe_access_token"] = token_data.get("access_token")
|
||||
session["adobe_refresh_token"] = token_data.get("refresh_token")
|
||||
session["adobe_auth_mode"] = "session_oauth"
|
||||
session = await _merge_adobe_profile(session, session["adobe_access_token"])
|
||||
|
||||
response = JSONResponse({"connected": True})
|
||||
save_session(response, session)
|
||||
|
|
@ -142,7 +198,7 @@ async def adobe_exchange(body: AdobeExchangeRequest, request: Request):
|
|||
|
||||
|
||||
@router.get("/adobe/connect")
|
||||
def adobe_connect_env(request: Request):
|
||||
async def adobe_connect_env(request: Request):
|
||||
"""
|
||||
Load Adobe Sign credentials directly from .env (ADOBE_ACCESS_TOKEN /
|
||||
ADOBE_REFRESH_TOKEN). Refreshes the token if needed. No browser login required
|
||||
|
|
@ -173,6 +229,7 @@ def adobe_connect_env(request: Request):
|
|||
session["adobe_access_token"] = token
|
||||
session["adobe_refresh_token"] = refresh_token
|
||||
session["adobe_auth_mode"] = "shared_env"
|
||||
session = await _merge_adobe_profile(session, token)
|
||||
|
||||
response = JSONResponse({"connected": True})
|
||||
save_session(response, session)
|
||||
|
|
@ -184,6 +241,10 @@ def adobe_disconnect(request: Request):
|
|||
session = get_session(request)
|
||||
session.pop("adobe_access_token", None)
|
||||
session.pop("adobe_refresh_token", None)
|
||||
session.pop("adobe_user_name", None)
|
||||
session.pop("adobe_user_email", None)
|
||||
session.pop("adobe_account_name", None)
|
||||
session.pop("adobe_account_id", None)
|
||||
session["adobe_auth_mode"] = "disconnected"
|
||||
response = JSONResponse({"disconnected": "adobe"})
|
||||
save_session(response, session)
|
||||
|
|
|
|||
|
|
@ -155,6 +155,10 @@ def session_public_view(session: dict[str, Any]) -> dict[str, Any]:
|
|||
"adobe": bool(session.get("adobe_access_token")),
|
||||
"docusign": bool(session.get("docusign_access_token")),
|
||||
"adobe_auth_mode": session.get("adobe_auth_mode", "disconnected"),
|
||||
"adobe_user_name": session.get("adobe_user_name"),
|
||||
"adobe_user_email": session.get("adobe_user_email"),
|
||||
"adobe_account_name": session.get("adobe_account_name"),
|
||||
"adobe_account_id": session.get("adobe_account_id"),
|
||||
"docusign_auth_mode": session.get("docusign_auth_mode", "disconnected"),
|
||||
"docusign_user_name": session.get("docusign_user_name"),
|
||||
"docusign_user_email": session.get("docusign_user_email"),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ export async function refreshAuth() {
|
|||
adobe: !!data.adobe,
|
||||
docusign: !!data.docusign,
|
||||
adobeLabel: data.adobe_label || 'Adobe Sign',
|
||||
adobeAccountId: data.adobe_account_id || null,
|
||||
adobeAccountName: data.adobe_account_name || null,
|
||||
docusignLabel: data.docusign_label || 'Docusign',
|
||||
docusignAccountId: data.docusign_account_id || null,
|
||||
docusignAccountName: data.docusign_account_name || null,
|
||||
|
|
@ -31,11 +33,16 @@ export async function refreshAuth() {
|
|||
// ── Render connection pills in top bar ─────────────────────────────────────
|
||||
|
||||
export function renderAuthChips() {
|
||||
renderChip('chip-adobe', state.auth.adobe, 'Adobe Sign', onClickAdobe);
|
||||
renderChip(
|
||||
'chip-adobe',
|
||||
state.auth.adobe,
|
||||
state.auth.adobe ? `Adobe: ${state.auth.adobeAccountName || state.auth.adobeLabel || 'Connected'}` : 'Adobe Sign',
|
||||
onClickAdobe
|
||||
);
|
||||
renderChip(
|
||||
'chip-docusign',
|
||||
state.auth.docusign,
|
||||
state.auth.docusignAccountName || 'Docusign',
|
||||
state.auth.docusign ? `Docusign: ${state.auth.docusignAccountName || state.auth.docusignLabel || 'Connected'}` : 'Docusign',
|
||||
onClickDocusign
|
||||
);
|
||||
renderAvatar();
|
||||
|
|
@ -97,7 +104,13 @@ export async function disconnectPlatform(platform, opts = {}) {
|
|||
docusignAccountSelectionRequired: false,
|
||||
});
|
||||
} else {
|
||||
setState('auth', { ...state.auth, adobe: false });
|
||||
setState('auth', {
|
||||
...state.auth,
|
||||
adobe: false,
|
||||
adobeAccountId: null,
|
||||
adobeAccountName: null,
|
||||
adobeLabel: 'Adobe Sign',
|
||||
});
|
||||
}
|
||||
renderAuthChips();
|
||||
if (!skipRefresh) {
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export function quickStartCardMarkup() {
|
|||
<div class="card-body">
|
||||
<div class="callout info" style="margin-bottom:16px">
|
||||
<span class="callout-icon">ℹ️</span>
|
||||
This tool compares Adobe Sign templates with DocuSign templates, helps you migrate them, and gives you a place to verify the results afterward.
|
||||
This app helps you migrate templates from Adobe Sign into DocuSign, review blockers and warnings, and send verification envelopes after migration.
|
||||
</div>
|
||||
${quickStartStepsMarkup()}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -193,14 +193,14 @@ async function _loadConnInfo() {
|
|||
connEl.innerHTML = `
|
||||
<div class="conn-info-row">
|
||||
<span class="conn-info-label">Adobe Sign</span>
|
||||
<span class="conn-info-value">${data.adobe ? 'Connected' : 'Not connected'}</span>
|
||||
<span class="conn-info-value">${data.adobe ? `Connected${data.adobe_account_name ? ` — ${escHtml(data.adobe_account_name)}` : ''}` : 'Not connected'}</span>
|
||||
<span class="conn-info-status">
|
||||
<span class="badge ${data.adobe ? 'badge-green' : 'badge-gray'}">${data.adobe ? '● Connected' : '○ Disconnected'}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="conn-info-row">
|
||||
<span class="conn-info-label">Docusign</span>
|
||||
<span class="conn-info-value">${data.docusign ? (data.docusign_account_selection_required ? 'Connected — account selection required' : 'Connected') : 'Not connected'}</span>
|
||||
<span class="conn-info-value">${data.docusign ? (data.docusign_account_selection_required ? 'Connected — account selection required' : `Connected${data.docusign_account_name ? ` — ${escHtml(data.docusign_account_name)}` : ''}`) : 'Not connected'}</span>
|
||||
<span class="conn-info-status">
|
||||
<span class="badge ${data.docusign ? (data.docusign_account_selection_required ? 'badge-amber' : 'badge-green') : 'badge-gray'}">${data.docusign ? (data.docusign_account_selection_required ? '● Choose account' : '● Connected') : '○ Disconnected'}</span>
|
||||
</span>
|
||||
|
|
@ -241,6 +241,11 @@ async function _loadConnInfo() {
|
|||
<span class="conn-info-value">Use <strong>Choose Account</strong> or <strong>Switch Docusign Account</strong> to select from the DocuSign accounts available to this login. The picker is sorted alphabetically and supports search.</span>
|
||||
<span class="conn-info-status"></span>
|
||||
</div>
|
||||
<div class="conn-info-row">
|
||||
<span class="conn-info-label">Adobe Account ID</span>
|
||||
<span class="conn-info-value mono">${escHtml(data.adobe_account_id || '—')}</span>
|
||||
<span class="conn-info-status"></span>
|
||||
</div>
|
||||
<div class="conn-info-row">
|
||||
<span class="conn-info-label">Docusign Account ID</span>
|
||||
<span class="conn-info-value mono">${escHtml(data.docusign_account_id || '—')}</span>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ export const state = {
|
|||
adobe: false,
|
||||
docusign: false,
|
||||
adobeLabel: 'Adobe Sign',
|
||||
adobeAccountId: null,
|
||||
adobeAccountName: null,
|
||||
docusignLabel: 'Docusign',
|
||||
docusignAccountId: null,
|
||||
docusignAccountName: null,
|
||||
|
|
|
|||
Loading…
Reference in New Issue