feat: ConnectWise PSA integration (#106)
PSA abstraction layer with provider pattern, ConnectWise integration (connection management, ticket linking, note posting, status updates, member mapping), Integrations page UI, Fernet credential encryption, in-memory TTL cache, 6 DB migrations, ConnectWise API reference docs.
This commit was merged in pull request #106.
This commit is contained in:
51
backend/app/services/psa/registry.py
Normal file
51
backend/app/services/psa/registry.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""Factory for instantiating PSA providers from stored connection data."""
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.psa_connection import PsaConnection
|
||||
from app.services.psa.base import PSAProvider
|
||||
from app.core.config import settings
|
||||
from app.services.psa.encryption import decrypt_credentials
|
||||
from app.services.psa.exceptions import PSAConnectionError
|
||||
|
||||
|
||||
async def get_provider_for_account(
|
||||
account_id: UUID, db: AsyncSession
|
||||
) -> PSAProvider:
|
||||
"""Look up account's PSA connection, decrypt credentials, instantiate provider."""
|
||||
result = await db.execute(
|
||||
select(PsaConnection).where(
|
||||
PsaConnection.account_id == account_id,
|
||||
PsaConnection.is_active.is_(True),
|
||||
)
|
||||
)
|
||||
connection = result.scalar_one_or_none()
|
||||
|
||||
if not connection:
|
||||
raise PSAConnectionError(
|
||||
"No active PSA connection configured for this account.",
|
||||
provider="unknown",
|
||||
)
|
||||
|
||||
if connection.provider == "connectwise":
|
||||
from app.services.psa.connectwise.client import ConnectWiseClient
|
||||
from app.services.psa.connectwise.provider import ConnectWiseProvider
|
||||
|
||||
creds = decrypt_credentials(connection.credentials_encrypted)
|
||||
client = ConnectWiseClient(
|
||||
site_url=connection.site_url,
|
||||
company_id=connection.company_id,
|
||||
public_key=creds["public_key"],
|
||||
private_key=creds["private_key"],
|
||||
client_id=settings.CW_CLIENT_ID or "",
|
||||
)
|
||||
return ConnectWiseProvider(client)
|
||||
|
||||
raise PSAConnectionError(
|
||||
f"Unsupported PSA provider: {connection.provider}",
|
||||
provider=connection.provider,
|
||||
)
|
||||
Reference in New Issue
Block a user