Ë
    §‘ˆjû!  ã                   ó  — d Z ddlZddlZddlZddlmZmZ ddlm	Z	  ej                  e«      ZdZdZdZi Z G d„ d	e«      Zd
efd„Zdefd„Zdefd„Zdededededed
efd„Zdededed
dfd„Zdedefd„Z	 ddedededed
df
d„Zy)a?  Reads and writes the platform's integration tables.

Those tables belong to the Node application and are managed by its Sequelize
migrations. Every access is funnelled through this module so the cross-service
coupling sits in one reviewable place, and so a schema change over there breaks
one file rather than several.
é    N)ÚJsonÚRealDictCursor)Úget_master_db_connectionÚorg_Úintegration_oauth_connecti,  c                   ó   — e Zd ZdZy)ÚTenantHasNoUserErrorzBintegrations.user is NOT NULL, and this tenant has no owning user.N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© ó    úA/var/www/html/strategist-ai/app/services/integrations/platform.pyr	   r	      s   „ ÚLr   r	   Úreturnc                 óä   — | rt        | t        «      st        d«      ‚| j                  t        «      st        d| ›d�«      ‚| t        t        «      d  }t        t        j                  |«      «      S )Nz$tenant_id must be a non-empty stringz
tenant_id z is missing the org_ prefix)Ú
isinstanceÚstrÚ
ValueErrorÚ
startswithÚTENANT_PREFIXÚlenÚuuid_libÚUUID)Ú	tenant_idÚraws     r   Ústrip_tenant_prefixr   #   se   € ÙœJ y´#Ô6ÜÐ?Ó@Ð@Ø×Ñ¤Ô.Ü˜: i ]Ð2MÐNÓOÐOØ
”CœÓ&Ð'Ð
(€Cô Œx�}‰}˜SÓ!Ó"Ð"r   Úplatformc                 óV  — t         j                  | «      }|�|\  }}t        j                  «       |k  r|S d }	 t	        «       }|j                  t        ¬«      5 }|j                  d| f«       |j                  «       }|sd n|d   |d   |d   |d   dœ}d d d «       |�|j                  «        	 t        j                  «       t        z   ft         | <   |S # 1 sw Y   ŒBxY w# t        $ r& d t        j                  «       t        z   ft         | <   ‚ w xY w# |�|j                  «        w w xY w)N©Úcursor_factoryzwSELECT "clientId", "clientSecret", "redirectUri", scope FROM platform_configs WHERE platform = %s AND "isActive" = trueÚclientIdÚclientSecretÚredirectUriÚscope)Ú	client_idÚclient_secretÚredirect_urir%   )Ú_config_cacheÚgetÚtimeÚ	monotonicr   Úcursorr   ÚexecuteÚfetchoneÚ	ExceptionÚ_CONFIG_CACHE_TTL_SECONDSÚclose)r   ÚcachedÚvalueÚ
expires_atÚconnÚcurÚrows          r   Úget_platform_configr9   .   s5  € Ü×Ñ˜xÓ(€FØÐØ"ÑˆˆzÜ�>‰>Ó˜jÒ(ØˆLà€DðÜ'Ó)ˆØ�[‰[¬ˆ[Ó7ð 
	'¸3Ø�K‰KðRà�ôð —,‘,“.ˆCÙ #‘DØ  ™_Ø!$ ^Ñ!4Ø # MÑ 2Ø˜W™ñ	*'ˆE÷
	'ð& ÐØ�J‰J�Là$¤d§n¡nÓ&6Ô9RÑ&RÐS„M�(ÑØ€L÷/
	'ð 
	'ûô ò ð
 $(¬¯©Ó)9Ô<UÑ)UÐ"VŒ�hÑØðûð ÐØ�J‰J�Lð ús/   ¹ C! Á;CÂC! ÃCÃC! Ã!/DÄD ÄD(Útenant_uuidc                 ó4  — t        «       }	 |j                  «       5 }|j                  d| f«       |j                  «       }|r
|d   r|d   nd cd d d «       |j	                  «        S # 1 sw Y   nxY w	 |j	                  «        y # |j	                  «        w xY w)Nz,SELECT "userId" FROM "Tenants" WHERE id = %sr   )r   r-   r.   r/   r2   )r:   r6   r7   r8   s       r   Úresolve_tenant_userr<   R   s†   € Ü#Ó%€DðØ�[‰[‹]ð 	6˜cØ�K‰KÐFÈÈÔWØ—,‘,“.ˆCÙ  S¨¢V�3�q’6°÷	6ð 	6ð
 	�
‰
�÷	6ð 	6úð 	6ð
 	�
‰
�øˆ�
‰
�ús"   ŒB œ1A'Á	B Á'A0Á,B ÂBr   ÚproviderÚ	auth_typeÚaccess_token_ciphertextÚmetadatac                 ó  — t        | «      }t        |«      }|st        d|› d�«      ‚t        |xs i «      }d|d<   t	        «       }	 |j                  «       5 }	|	j                  d||f«       |	j                  «       }
|
r2|	j                  d||t        |«      d|
d   f«       t        |
d   «      }n@t        t        j                  «       «      }|	j                  d	|||||t        |«      d|f«       d d d «       |j                  «        t        j                  d
||«       |j                  «        S # 1 sw Y   ŒBxY w# t         $ r+ |j#                  «        t        j%                  d||d¬«       ‚ w xY w# |j                  «        w xY w)NzTenant z- has no userId; integrations.user is NOT NULLÚfernetÚtoken_encodingzCSELECT id FROM integrations WHERE "tenantId" = %s AND provider = %szzUPDATE integrations SET "accessToken" = %s, "authType" = %s, metadata = %s, status = %s, "updatedAt" = now() WHERE id = %sÚACTIVEr   z±INSERT INTO integrations (id, "user", provider, "authType", "accessToken", metadata, status, "tenantId", "createdAt", "updatedAt") VALUES (%s,%s,%s,%s,%s,%s,%s,%s, now(), now())z%Recorded %s integration for tenant %sz-Could not record %s integration for tenant %sT©Úexc_info)r   r<   r	   Údictr   r-   r.   r/   r   r   r   Úuuid4ÚcommitÚloggerÚinfor2   r0   ÚrollbackÚerror)r   r=   r>   r?   r@   r:   Úuser_idÚpayloadr6   r7   ÚexistingÚintegration_ids               r   Úupsert_integrationrR   ]   s”  € ä% iÓ0€KÜ! +Ó.€GÙÜ"Ø�k�]Ð"OÐPóRð 	Rô �8’>˜rÓ"€Gð !)€GÐÑä#Ó%€DðØ�[‰[‹]ð 	U˜cØ�K‰KØUØ˜hÐ'ô)ð —|‘|“~ˆHáØ—‘ðTà,¨i¼¸g»Ø˜x¨™{ð,ô-ô
 "% X¨a¡[Ó!1‘ä!$¤X§^¡^Ó%5Ó!6�Ø—‘ðRð $ W¨h¸	Ø,¬d°7«m¸XÀ{ðTô	U÷	Uð* 	�‰ŒÜ�‰Ð;¸XÀ{ÔSØð 	�
‰
�÷=	Uð 	Uûô0 ò Ø�‰ŒÜ�‰ÐDØ˜{°Tð 	ô 	;àð	ûð 	�
‰
�ús1   ÁD= ÁBD1Ã00D= Ä1D:Ä6D= Ä=4E1Å1E4 Å4FÚstatusc                 ó~  — t        | «      }t        «       }	 |j                  «       5 }|j                  d|||f«       d d d «       |j	                  «        	 |j                  «        y # 1 sw Y   Œ+xY w# t
        $ r+ |j                  «        t        j                  d||d¬«       ‚ w xY w# |j                  «        w xY w)Nz`UPDATE integrations SET status = %s, "updatedAt" = now() WHERE "tenantId" = %s AND provider = %sz%Could not set %s status for tenant %sTrE   )
r   r   r-   r.   rI   r0   rL   rJ   rM   r2   )r   r=   rS   r:   r6   r7   s         r   Úset_integration_statusrU   �   s­   € Ü% iÓ0€KÜ#Ó%€DðØ�[‰[‹]ð 	1˜cØ�K‰Kð:à˜ hÐ/ô1÷	1ð
 	�‰�ð 	�
‰
�÷	1ð 	1ûô ò Ø�‰ŒÜ�‰Ð<¸hÈØ"ð 	ô 	$àð	ûð 	�
‰
�ús-   —A3 §A'½A3 Á'A0Á,A3 Á34B'Â'B* Â*B<c                 óZ  — t        | «      }t        «       }	 |j                  t        ¬«      5 }|j	                  d||f«       |j                  «       }|rt        |«      ndcddd«       |j                  «        S # 1 sw Y   nxY w	 |j                  «        y# |j                  «        w xY w)zŽNever returns accessToken -- callers that need the token read it from
    product_sources, where it is stored with the rest of the sync state.r    z…SELECT id, provider, "authType", status, metadata, "createdAt", "updatedAt" FROM integrations WHERE "tenantId" = %s AND provider = %sN)r   r   r-   r   r.   r/   rG   r2   )r   r=   r:   r6   r7   r8   s         r   Úget_integrationrW       sœ   € ô & iÓ0€KÜ#Ó%€Dð	Ø�[‰[¬ˆ[Ó7ð 	.¸3Ø�K‰KðXà˜hÐ'ô)ð —,‘,“.ˆCÙ #”4˜”9¨÷	.ð 	.ð 	�
‰
�÷	.ð 	.úð 	.ð 	�
‰
�øˆ�
‰
�ús"   —B ­3A:Á 	B Á:BÁ?B ÂB*Údelivery_statusÚoutcomec                 ó2  — 	 t        | «      }t        ||dœ}|j                  |xs i «       d}	 t        «       }|j                  «       5 }	|	j                  d|||||t        |«      f«       ddd«       |j                  «        |�|j                  «        yy# t        $ r t        j                  d| «       Y yw xY w# 1 sw Y   ŒRxY w# t        $ r- |�|j                  «        t        j                  d|d¬«       Y Œzw xY w# |�|j                  «        w w xY w)z�Best-effort audit. A tracker failure must never fail a connection that
    otherwise succeeded, so this logs and returns rather than raising.z,Cannot track connect for malformed tenant %rN)ÚjobNameÚproviderRawÚconnectStepz¶INSERT INTO integration_connect_tracker ("tenantId", platform, outcome, "deliveryStatus", "errorMessage", metadata, "createdAt", "updatedAt") VALUES (%s,%s,%s,%s,%s,%s, now(), now())z*Could not write connect tracker row for %sTrE   )r   r   rJ   ÚwarningÚJOB_NAMEÚupdater   r-   r.   r   rI   r0   rL   rM   r2   )
r   r   rX   rY   Úerror_messageÚextrar:   r@   r6   r7   s
             r   Útrack_connectrc   ±   s$  € ðÜ)¨)Ó4ˆô
 $°HØ.ñ0€Hà‡O�O�E’K˜RÔ ð €DðÜ'Ó)ˆØ�[‰[‹]ð 	1˜cØ�K‰KðHð ˜h¨°Ø¤ X£ð0ô	1÷	1ð 	�‰Œð ÐØ�J‰J�Lð øô= ò Ü�‰ÐEÀyÔQÙðú÷ 	1ð 	1ûô ò $ØÐØ�M‰MŒOÜ�‰ÐAÀ8Ø"ð 	ö 	$ð$ûð ÐØ�J‰J�Lð úsL   ‚B ¯C Á	"B<Á+C ÂB9Â8B9Â<CÃC Ã3C>Ã;D Ã=C>Ã>D ÄD)NN)r   Úloggingr+   Úuuidr   Úpsycopg2.extrasr   r   Úapp.services.infra.databaser   Ú	getLoggerr
   rJ   r   r_   r1   r)   r0   r	   r   r   r9   r<   rG   rR   rU   rW   rc   r   r   r   ú<module>ri      s  ðñó Û Û ç 0å @à	ˆ×	Ñ	˜8Ó	$€à€Ø&€ð  Ð Ø€ôM˜9ô Mð# có #ð! #ó !ðH Só ð- #ð -°ð -Àð -Ø03ð-Ø?Cð-ØHKó-ð` cð °Sð À#ð È$ó ð&˜sð ¨có ð$ ;?ñ%˜Sð %¨Cð %À#ð %Øð%ØCGô%r   