volcano-sdk-python 0.6.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (778) hide show
  1. volcano_sdk/__init__.py +91 -0
  2. volcano_sdk/_function_resolution.py +159 -0
  3. volcano_sdk/_generated/__init__.py +8 -0
  4. volcano_sdk/_generated/api/__init__.py +1 -0
  5. volcano_sdk/_generated/api/anon_keys/__init__.py +1 -0
  6. volcano_sdk/_generated/api/anon_keys/create_anon_key.py +192 -0
  7. volcano_sdk/_generated/api/anon_keys/get_anon_key.py +197 -0
  8. volcano_sdk/_generated/api/anon_keys/list_anon_keys.py +297 -0
  9. volcano_sdk/_generated/api/anon_keys/regenerate_anon_key.py +193 -0
  10. volcano_sdk/_generated/api/anon_keys/revoke_anon_key.py +216 -0
  11. volcano_sdk/_generated/api/anon_keys/set_default_anon_key.py +217 -0
  12. volcano_sdk/_generated/api/auth_admin/__init__.py +1 -0
  13. volcano_sdk/_generated/api/auth_admin/ban_auth_user.py +240 -0
  14. volcano_sdk/_generated/api/auth_admin/delete_all_user_sessions.py +132 -0
  15. volcano_sdk/_generated/api/auth_admin/delete_auth_user.py +127 -0
  16. volcano_sdk/_generated/api/auth_admin/delete_user_session.py +139 -0
  17. volcano_sdk/_generated/api/auth_admin/get_auth_insights.py +301 -0
  18. volcano_sdk/_generated/api/auth_admin/get_auth_user.py +185 -0
  19. volcano_sdk/_generated/api/auth_admin/list_auth_users.py +306 -0
  20. volcano_sdk/_generated/api/auth_admin/list_user_sessions.py +365 -0
  21. volcano_sdk/_generated/api/auth_admin/unban_auth_user.py +205 -0
  22. volcano_sdk/_generated/api/auth_configuration/__init__.py +1 -0
  23. volcano_sdk/_generated/api/auth_configuration/configure_auth_methods.py +143 -0
  24. volcano_sdk/_generated/api/auth_configuration/create_email_template.py +246 -0
  25. volcano_sdk/_generated/api/auth_configuration/delete_auth_page_layout.py +219 -0
  26. volcano_sdk/_generated/api/auth_configuration/delete_auth_page_theme.py +197 -0
  27. volcano_sdk/_generated/api/auth_configuration/delete_email_template.py +211 -0
  28. volcano_sdk/_generated/api/auth_configuration/get_auth_config.py +172 -0
  29. volcano_sdk/_generated/api/auth_configuration/get_auth_hosted_page.py +207 -0
  30. volcano_sdk/_generated/api/auth_configuration/get_auth_methods.py +184 -0
  31. volcano_sdk/_generated/api/auth_configuration/get_auth_page_appearance.py +201 -0
  32. volcano_sdk/_generated/api/auth_configuration/get_default_email_template.py +177 -0
  33. volcano_sdk/_generated/api/auth_configuration/get_default_email_templates.py +158 -0
  34. volcano_sdk/_generated/api/auth_configuration/get_email_template.py +191 -0
  35. volcano_sdk/_generated/api/auth_configuration/get_hosted_login_options.py +221 -0
  36. volcano_sdk/_generated/api/auth_configuration/hosted_login_check_email.py +231 -0
  37. volcano_sdk/_generated/api/auth_configuration/list_email_templates.py +180 -0
  38. volcano_sdk/_generated/api/auth_configuration/preview_auth_page.py +243 -0
  39. volcano_sdk/_generated/api/auth_configuration/render_auth_page_preview.py +228 -0
  40. volcano_sdk/_generated/api/auth_configuration/render_default_managed_auth_page.py +261 -0
  41. volcano_sdk/_generated/api/auth_configuration/render_managed_auth_page.py +215 -0
  42. volcano_sdk/_generated/api/auth_configuration/test_email_config.py +292 -0
  43. volcano_sdk/_generated/api/auth_configuration/update_auth_config.py +219 -0
  44. volcano_sdk/_generated/api/auth_configuration/update_auth_hosted_page.py +243 -0
  45. volcano_sdk/_generated/api/auth_configuration/update_auth_page_layout.py +242 -0
  46. volcano_sdk/_generated/api/auth_configuration/update_auth_page_theme.py +227 -0
  47. volcano_sdk/_generated/api/auth_configuration/update_email_template.py +239 -0
  48. volcano_sdk/_generated/api/authentication/__init__.py +1 -0
  49. volcano_sdk/_generated/api/authentication/auth_cancel_email_change.py +158 -0
  50. volcano_sdk/_generated/api/authentication/auth_confirm_email.py +198 -0
  51. volcano_sdk/_generated/api/authentication/auth_confirm_email_change.py +215 -0
  52. volcano_sdk/_generated/api/authentication/auth_convert_anonymous.py +222 -0
  53. volcano_sdk/_generated/api/authentication/auth_delete_all_my_sessions.py +166 -0
  54. volcano_sdk/_generated/api/authentication/auth_delete_my_session.py +195 -0
  55. volcano_sdk/_generated/api/authentication/auth_forgot_password.py +209 -0
  56. volcano_sdk/_generated/api/authentication/auth_get_my_sessions.py +397 -0
  57. volcano_sdk/_generated/api/authentication/auth_get_password_policy.py +181 -0
  58. volcano_sdk/_generated/api/authentication/auth_get_user.py +166 -0
  59. volcano_sdk/_generated/api/authentication/auth_list_identities.py +178 -0
  60. volcano_sdk/_generated/api/authentication/auth_list_methods.py +174 -0
  61. volcano_sdk/_generated/api/authentication/auth_logout.py +138 -0
  62. volcano_sdk/_generated/api/authentication/auth_promote_method.py +221 -0
  63. volcano_sdk/_generated/api/authentication/auth_refresh.py +235 -0
  64. volcano_sdk/_generated/api/authentication/auth_request_email_change.py +226 -0
  65. volcano_sdk/_generated/api/authentication/auth_resend_confirmation.py +210 -0
  66. volcano_sdk/_generated/api/authentication/auth_reset_password.py +206 -0
  67. volcano_sdk/_generated/api/authentication/auth_signin.py +222 -0
  68. volcano_sdk/_generated/api/authentication/auth_signup.py +254 -0
  69. volcano_sdk/_generated/api/authentication/auth_signup_anonymous.py +205 -0
  70. volcano_sdk/_generated/api/authentication/auth_unlink_identity.py +206 -0
  71. volcano_sdk/_generated/api/authentication/auth_update_user.py +211 -0
  72. volcano_sdk/_generated/api/database_backups/__init__.py +1 -0
  73. volcano_sdk/_generated/api/database_backups/create_database_backup.py +265 -0
  74. volcano_sdk/_generated/api/database_backups/create_database_restore.py +305 -0
  75. volcano_sdk/_generated/api/database_backups/delete_database_backup.py +247 -0
  76. volcano_sdk/_generated/api/database_backups/get_database_backup.py +235 -0
  77. volcano_sdk/_generated/api/database_backups/get_database_backup_schedule.py +226 -0
  78. volcano_sdk/_generated/api/database_backups/get_database_restore.py +232 -0
  79. volcano_sdk/_generated/api/database_backups/list_database_backups.py +242 -0
  80. volcano_sdk/_generated/api/database_backups/list_database_restores.py +223 -0
  81. volcano_sdk/_generated/api/database_backups/update_database_backup_schedule.py +272 -0
  82. volcano_sdk/_generated/api/database_branches/__init__.py +1 -0
  83. volcano_sdk/_generated/api/database_branches/create_database_branch.py +285 -0
  84. volcano_sdk/_generated/api/database_branches/delete_database_branch.py +249 -0
  85. volcano_sdk/_generated/api/database_branches/get_database_branch.py +225 -0
  86. volcano_sdk/_generated/api/database_branches/list_database_branches.py +224 -0
  87. volcano_sdk/_generated/api/database_branches/reset_database_branch.py +268 -0
  88. volcano_sdk/_generated/api/database_branches/reset_database_branch_password.py +252 -0
  89. volcano_sdk/_generated/api/database_branches/update_database_branch.py +267 -0
  90. volcano_sdk/_generated/api/database_queries/__init__.py +1 -0
  91. volcano_sdk/_generated/api/database_queries/query_database_branch_delete.py +295 -0
  92. volcano_sdk/_generated/api/database_queries/query_database_branch_insert.py +299 -0
  93. volcano_sdk/_generated/api/database_queries/query_database_branch_ping.py +276 -0
  94. volcano_sdk/_generated/api/database_queries/query_database_branch_select.py +315 -0
  95. volcano_sdk/_generated/api/database_queries/query_database_branch_update.py +303 -0
  96. volcano_sdk/_generated/api/database_queries/query_database_delete.py +259 -0
  97. volcano_sdk/_generated/api/database_queries/query_database_insert.py +263 -0
  98. volcano_sdk/_generated/api/database_queries/query_database_ping.py +240 -0
  99. volcano_sdk/_generated/api/database_queries/query_database_select.py +279 -0
  100. volcano_sdk/_generated/api/database_queries/query_database_update.py +267 -0
  101. volcano_sdk/_generated/api/databases/__init__.py +1 -0
  102. volcano_sdk/_generated/api/databases/create_database.py +236 -0
  103. volcano_sdk/_generated/api/databases/delete_database.py +247 -0
  104. volcano_sdk/_generated/api/databases/get_database.py +185 -0
  105. volcano_sdk/_generated/api/databases/get_database_stats.py +292 -0
  106. volcano_sdk/_generated/api/databases/get_project_database_queries.py +267 -0
  107. volcano_sdk/_generated/api/databases/list_database_regions.py +167 -0
  108. volcano_sdk/_generated/api/databases/list_databases.py +314 -0
  109. volcano_sdk/_generated/api/databases/list_postgres_versions.py +167 -0
  110. volcano_sdk/_generated/api/databases/reset_database_password.py +250 -0
  111. volcano_sdk/_generated/api/databases/update_database_type.py +268 -0
  112. volcano_sdk/_generated/api/frontends/__init__.py +1 -0
  113. volcano_sdk/_generated/api/frontends/create_frontend.py +369 -0
  114. volcano_sdk/_generated/api/frontends/create_frontend_custom_domain.py +278 -0
  115. volcano_sdk/_generated/api/frontends/delete_frontend.py +244 -0
  116. volcano_sdk/_generated/api/frontends/delete_frontend_custom_domain.py +217 -0
  117. volcano_sdk/_generated/api/frontends/get_frontend.py +221 -0
  118. volcano_sdk/_generated/api/frontends/get_frontend_custom_domain.py +234 -0
  119. volcano_sdk/_generated/api/frontends/get_frontend_usage_history.py +283 -0
  120. volcano_sdk/_generated/api/frontends/list_frontend_deployments.py +258 -0
  121. volcano_sdk/_generated/api/frontends/list_frontends.py +329 -0
  122. volcano_sdk/_generated/api/frontends/list_project_custom_domains.py +318 -0
  123. volcano_sdk/_generated/api/frontends/redeploy_frontend.py +267 -0
  124. volcano_sdk/_generated/api/functions/__init__.py +1 -0
  125. volcano_sdk/_generated/api/functions/create_function.py +320 -0
  126. volcano_sdk/_generated/api/functions/create_function_scheduler.py +225 -0
  127. volcano_sdk/_generated/api/functions/create_functions_batch.py +271 -0
  128. volcano_sdk/_generated/api/functions/delete_function.py +209 -0
  129. volcano_sdk/_generated/api/functions/delete_function_scheduler.py +130 -0
  130. volcano_sdk/_generated/api/functions/get_function.py +193 -0
  131. volcano_sdk/_generated/api/functions/get_function_scheduler.py +198 -0
  132. volcano_sdk/_generated/api/functions/invoke_function.py +385 -0
  133. volcano_sdk/_generated/api/functions/list_function_deployments.py +230 -0
  134. volcano_sdk/_generated/api/functions/list_function_regions.py +171 -0
  135. volcano_sdk/_generated/api/functions/list_function_runtimes.py +166 -0
  136. volcano_sdk/_generated/api/functions/list_function_schedulers.py +185 -0
  137. volcano_sdk/_generated/api/functions/list_functions.py +301 -0
  138. volcano_sdk/_generated/api/functions/list_project_schedulers.py +289 -0
  139. volcano_sdk/_generated/api/functions/resolve_function_for_invocation.py +264 -0
  140. volcano_sdk/_generated/api/functions/update_function.py +220 -0
  141. volcano_sdk/_generated/api/functions/update_function_scheduler.py +218 -0
  142. volcano_sdk/_generated/api/git_connections/__init__.py +1 -0
  143. volcano_sdk/_generated/api/git_connections/delete_git_connection.py +204 -0
  144. volcano_sdk/_generated/api/git_connections/git_connect_callback.py +247 -0
  145. volcano_sdk/_generated/api/git_connections/list_git_connections.py +172 -0
  146. volcano_sdk/_generated/api/git_connections/list_git_installation_repositories.py +237 -0
  147. volcano_sdk/_generated/api/git_connections/list_git_installations.py +224 -0
  148. volcano_sdk/_generated/api/git_connections/start_git_connect.py +250 -0
  149. volcano_sdk/_generated/api/locks/__init__.py +1 -0
  150. volcano_sdk/_generated/api/locks/acquire_project_lock.py +289 -0
  151. volcano_sdk/_generated/api/locks/force_release_project_lock.py +254 -0
  152. volcano_sdk/_generated/api/locks/get_project_lock.py +246 -0
  153. volcano_sdk/_generated/api/locks/release_project_lock.py +252 -0
  154. volcano_sdk/_generated/api/locks/renew_project_lock.py +285 -0
  155. volcano_sdk/_generated/api/logs/__init__.py +1 -0
  156. volcano_sdk/_generated/api/logs/get_project_log_activity.py +261 -0
  157. volcano_sdk/_generated/api/logs/search_project_logs.py +265 -0
  158. volcano_sdk/_generated/api/logs/stream_project_logs.py +345 -0
  159. volcano_sdk/_generated/api/o_auth_authentication/__init__.py +1 -0
  160. volcano_sdk/_generated/api/o_auth_authentication/auth_device_authorize.py +266 -0
  161. volcano_sdk/_generated/api/o_auth_authentication/auth_device_token.py +209 -0
  162. volcano_sdk/_generated/api/o_auth_authentication/auth_device_verify.py +230 -0
  163. volcano_sdk/_generated/api/o_auth_authentication/auth_link_o_auth_provider.py +272 -0
  164. volcano_sdk/_generated/api/o_auth_authentication/auth_list_o_auth_providers.py +166 -0
  165. volcano_sdk/_generated/api/o_auth_authentication/auth_o_auth_authorize.py +181 -0
  166. volcano_sdk/_generated/api/o_auth_authentication/auth_o_auth_callback.py +260 -0
  167. volcano_sdk/_generated/api/o_auth_authentication/auth_o_auth_exchange.py +210 -0
  168. volcano_sdk/_generated/api/o_auth_authentication/auth_platform_exchange.py +202 -0
  169. volcano_sdk/_generated/api/o_auth_authentication/auth_unlink_o_auth_provider.py +203 -0
  170. volcano_sdk/_generated/api/o_auth_authentication/call_o_auth_provider_api.py +309 -0
  171. volcano_sdk/_generated/api/o_auth_authentication/get_o_auth_provider_token.py +200 -0
  172. volcano_sdk/_generated/api/o_auth_authentication/refresh_o_auth_provider_token.py +207 -0
  173. volcano_sdk/_generated/api/o_auth_configuration/__init__.py +1 -0
  174. volcano_sdk/_generated/api/o_auth_configuration/create_o_auth_config.py +204 -0
  175. volcano_sdk/_generated/api/o_auth_configuration/delete_o_auth_config.py +142 -0
  176. volcano_sdk/_generated/api/o_auth_configuration/get_o_auth_config.py +209 -0
  177. volcano_sdk/_generated/api/o_auth_configuration/list_available_o_auth_providers.py +180 -0
  178. volcano_sdk/_generated/api/o_auth_configuration/list_o_auth_configs.py +180 -0
  179. volcano_sdk/_generated/api/o_auth_configuration/update_o_auth_config.py +230 -0
  180. volcano_sdk/_generated/api/project_imports/__init__.py +1 -0
  181. volcano_sdk/_generated/api/project_imports/complete_import_connect.py +322 -0
  182. volcano_sdk/_generated/api/project_imports/delete_import_connection.py +211 -0
  183. volcano_sdk/_generated/api/project_imports/get_project_import_run.py +216 -0
  184. volcano_sdk/_generated/api/project_imports/list_import_connections.py +165 -0
  185. volcano_sdk/_generated/api/project_imports/list_import_sources.py +261 -0
  186. volcano_sdk/_generated/api/project_imports/preflight_project_import.py +258 -0
  187. volcano_sdk/_generated/api/project_imports/start_import_connect.py +246 -0
  188. volcano_sdk/_generated/api/project_imports/start_project_import.py +284 -0
  189. volcano_sdk/_generated/api/projects/__init__.py +1 -0
  190. volcano_sdk/_generated/api/projects/apply_project_config.py +342 -0
  191. volcano_sdk/_generated/api/projects/cancel_project_source_export.py +231 -0
  192. volcano_sdk/_generated/api/projects/connect_project_git.py +278 -0
  193. volcano_sdk/_generated/api/projects/create_project.py +205 -0
  194. volcano_sdk/_generated/api/projects/delete_project.py +196 -0
  195. volcano_sdk/_generated/api/projects/delete_project_logo.py +213 -0
  196. volcano_sdk/_generated/api/projects/disconnect_project_git.py +204 -0
  197. volcano_sdk/_generated/api/projects/export_project_source.py +351 -0
  198. volcano_sdk/_generated/api/projects/get_project.py +180 -0
  199. volcano_sdk/_generated/api/projects/get_project_config.py +251 -0
  200. volcano_sdk/_generated/api/projects/get_project_git_connection.py +201 -0
  201. volcano_sdk/_generated/api/projects/get_project_git_deploy_settings.py +201 -0
  202. volcano_sdk/_generated/api/projects/get_project_health.py +221 -0
  203. volcano_sdk/_generated/api/projects/get_project_logo.py +196 -0
  204. volcano_sdk/_generated/api/projects/get_project_source_export.py +276 -0
  205. volcano_sdk/_generated/api/projects/get_project_usage.py +199 -0
  206. volcano_sdk/_generated/api/projects/list_deployments.py +558 -0
  207. volcano_sdk/_generated/api/projects/list_project_deployments.py +358 -0
  208. volcano_sdk/_generated/api/projects/list_projects.py +332 -0
  209. volcano_sdk/_generated/api/projects/query_project_metrics.py +251 -0
  210. volcano_sdk/_generated/api/projects/set_project_git_production_branch.py +291 -0
  211. volcano_sdk/_generated/api/projects/summarize_project_deployments.py +288 -0
  212. volcano_sdk/_generated/api/projects/update_project.py +221 -0
  213. volcano_sdk/_generated/api/projects/update_project_git_deploy_settings.py +287 -0
  214. volcano_sdk/_generated/api/projects/upload_project_logo.py +248 -0
  215. volcano_sdk/_generated/api/realtime/__init__.py +1 -0
  216. volcano_sdk/_generated/api/realtime/get_realtime_config.py +195 -0
  217. volcano_sdk/_generated/api/realtime/get_realtime_stats.py +188 -0
  218. volcano_sdk/_generated/api/realtime/update_realtime_config.py +219 -0
  219. volcano_sdk/_generated/api/service_keys/__init__.py +1 -0
  220. volcano_sdk/_generated/api/service_keys/create_service_key.py +216 -0
  221. volcano_sdk/_generated/api/service_keys/delete_service_key.py +129 -0
  222. volcano_sdk/_generated/api/service_keys/get_service_key.py +197 -0
  223. volcano_sdk/_generated/api/service_keys/list_service_keys.py +285 -0
  224. volcano_sdk/_generated/api/service_keys/regenerate_service_key.py +201 -0
  225. volcano_sdk/_generated/api/storage_admin/__init__.py +1 -0
  226. volcano_sdk/_generated/api/storage_admin/get_storage_stats.py +180 -0
  227. volcano_sdk/_generated/api/storage_admin/list_storage_objects_admin.py +299 -0
  228. volcano_sdk/_generated/api/storage_buckets/__init__.py +1 -0
  229. volcano_sdk/_generated/api/storage_buckets/create_storage_bucket.py +196 -0
  230. volcano_sdk/_generated/api/storage_buckets/delete_storage_bucket.py +123 -0
  231. volcano_sdk/_generated/api/storage_buckets/get_storage_bucket.py +189 -0
  232. volcano_sdk/_generated/api/storage_buckets/list_storage_buckets.py +297 -0
  233. volcano_sdk/_generated/api/storage_buckets/update_storage_bucket.py +205 -0
  234. volcano_sdk/_generated/api/storage_objects/__init__.py +1 -0
  235. volcano_sdk/_generated/api/storage_objects/copy_storage_object.py +203 -0
  236. volcano_sdk/_generated/api/storage_objects/delete_storage_object.py +252 -0
  237. volcano_sdk/_generated/api/storage_objects/download_public_file.py +302 -0
  238. volcano_sdk/_generated/api/storage_objects/download_storage_object.py +283 -0
  239. volcano_sdk/_generated/api/storage_objects/list_storage_objects.py +235 -0
  240. volcano_sdk/_generated/api/storage_objects/move_storage_object.py +203 -0
  241. volcano_sdk/_generated/api/storage_objects/update_storage_object_visibility.py +244 -0
  242. volcano_sdk/_generated/api/storage_objects/upload_part.py +287 -0
  243. volcano_sdk/_generated/api/storage_objects/upload_storage_object.py +380 -0
  244. volcano_sdk/_generated/api/storage_policies/__init__.py +1 -0
  245. volcano_sdk/_generated/api/storage_policies/create_storage_policy.py +205 -0
  246. volcano_sdk/_generated/api/storage_policies/delete_storage_policy.py +130 -0
  247. volcano_sdk/_generated/api/storage_policies/list_storage_policies.py +190 -0
  248. volcano_sdk/_generated/api/system/__init__.py +1 -0
  249. volcano_sdk/_generated/api/system/health_check.py +153 -0
  250. volcano_sdk/_generated/api/variables/__init__.py +1 -0
  251. volcano_sdk/_generated/api/variables/create_variable.py +212 -0
  252. volcano_sdk/_generated/api/variables/delete_variable.py +201 -0
  253. volcano_sdk/_generated/api/variables/get_variable.py +201 -0
  254. volcano_sdk/_generated/api/variables/list_variables.py +285 -0
  255. volcano_sdk/_generated/api/variables/update_variable.py +225 -0
  256. volcano_sdk/_generated/client.py +271 -0
  257. volcano_sdk/_generated/errors.py +14 -0
  258. volcano_sdk/_generated/models/__init__.py +1003 -0
  259. volcano_sdk/_generated/models/anon_key.py +167 -0
  260. volcano_sdk/_generated/models/auth_cancel_email_change_response_200.py +78 -0
  261. volcano_sdk/_generated/models/auth_config.py +559 -0
  262. volcano_sdk/_generated/models/auth_config_allowed_email_domains_mode.py +10 -0
  263. volcano_sdk/_generated/models/auth_confirm_email_body.py +76 -0
  264. volcano_sdk/_generated/models/auth_confirm_email_change_body.py +76 -0
  265. volcano_sdk/_generated/models/auth_confirm_email_change_response_200.py +102 -0
  266. volcano_sdk/_generated/models/auth_confirm_email_response_200.py +92 -0
  267. volcano_sdk/_generated/models/auth_confirm_email_response_200_message.py +10 -0
  268. volcano_sdk/_generated/models/auth_convert_anonymous_body.py +110 -0
  269. volcano_sdk/_generated/models/auth_convert_anonymous_body_user_metadata.py +65 -0
  270. volcano_sdk/_generated/models/auth_convert_anonymous_response_200.py +93 -0
  271. volcano_sdk/_generated/models/auth_device_authorize_body.py +76 -0
  272. volcano_sdk/_generated/models/auth_device_token_body.py +98 -0
  273. volcano_sdk/_generated/models/auth_device_token_body_grant_type.py +10 -0
  274. volcano_sdk/_generated/models/auth_device_verify_body.py +100 -0
  275. volcano_sdk/_generated/models/auth_device_verify_body_action.py +10 -0
  276. volcano_sdk/_generated/models/auth_device_verify_response_200.py +87 -0
  277. volcano_sdk/_generated/models/auth_forgot_password_body.py +76 -0
  278. volcano_sdk/_generated/models/auth_forgot_password_response_200.py +78 -0
  279. volcano_sdk/_generated/models/auth_get_my_sessions_response_200.py +190 -0
  280. volcano_sdk/_generated/models/auth_get_my_sessions_sort.py +10 -0
  281. volcano_sdk/_generated/models/auth_get_my_sessions_status.py +10 -0
  282. volcano_sdk/_generated/models/auth_get_user_response_200.py +93 -0
  283. volcano_sdk/_generated/models/auth_hosted_page.py +188 -0
  284. volcano_sdk/_generated/models/auth_hosted_page_defaults.py +87 -0
  285. volcano_sdk/_generated/models/auth_hosted_page_response.py +133 -0
  286. volcano_sdk/_generated/models/auth_hosted_page_runtime.py +97 -0
  287. volcano_sdk/_generated/models/auth_identities_response.py +94 -0
  288. volcano_sdk/_generated/models/auth_identity.py +119 -0
  289. volcano_sdk/_generated/models/auth_insights_interval.py +10 -0
  290. volcano_sdk/_generated/models/auth_insights_response.py +128 -0
  291. volcano_sdk/_generated/models/auth_insights_series_point.py +87 -0
  292. volcano_sdk/_generated/models/auth_insights_summary.py +67 -0
  293. volcano_sdk/_generated/models/auth_insights_window.py +87 -0
  294. volcano_sdk/_generated/models/auth_link_o_auth_provider_provider.py +10 -0
  295. volcano_sdk/_generated/models/auth_link_o_auth_provider_response_200.py +78 -0
  296. volcano_sdk/_generated/models/auth_link_o_auth_provider_response_mode.py +10 -0
  297. volcano_sdk/_generated/models/auth_list_o_auth_providers_response_200.py +100 -0
  298. volcano_sdk/_generated/models/auth_list_o_auth_providers_response_200_providers_item.py +118 -0
  299. volcano_sdk/_generated/models/auth_logout_body.py +101 -0
  300. volcano_sdk/_generated/models/auth_logout_body_session_mode.py +10 -0
  301. volcano_sdk/_generated/models/auth_method_summary.py +173 -0
  302. volcano_sdk/_generated/models/auth_method_summary_type.py +10 -0
  303. volcano_sdk/_generated/models/auth_methods_response.py +94 -0
  304. volcano_sdk/_generated/models/auth_o_auth_authorize_provider.py +10 -0
  305. volcano_sdk/_generated/models/auth_o_auth_authorize_response_mode.py +10 -0
  306. volcano_sdk/_generated/models/auth_o_auth_callback_provider.py +10 -0
  307. volcano_sdk/_generated/models/auth_o_auth_exchange_body.py +84 -0
  308. volcano_sdk/_generated/models/auth_page_appearance_defaults.py +97 -0
  309. volcano_sdk/_generated/models/auth_page_appearance_options.py +207 -0
  310. volcano_sdk/_generated/models/auth_page_appearance_response.py +148 -0
  311. volcano_sdk/_generated/models/auth_page_appearance_response_layouts.py +78 -0
  312. volcano_sdk/_generated/models/auth_page_appearance_response_parked.py +66 -0
  313. volcano_sdk/_generated/models/auth_page_density.py +10 -0
  314. volcano_sdk/_generated/models/auth_page_font.py +10 -0
  315. volcano_sdk/_generated/models/auth_page_layout.py +10 -0
  316. volcano_sdk/_generated/models/auth_page_radius.py +10 -0
  317. volcano_sdk/_generated/models/auth_page_scale.py +10 -0
  318. volcano_sdk/_generated/models/auth_page_theme.py +131 -0
  319. volcano_sdk/_generated/models/auth_page_theme_colors.py +90 -0
  320. volcano_sdk/_generated/models/auth_page_theme_version.py +10 -0
  321. volcano_sdk/_generated/models/auth_password_policy.py +116 -0
  322. volcano_sdk/_generated/models/auth_platform_exchange_body.py +76 -0
  323. volcano_sdk/_generated/models/auth_refresh_body.py +101 -0
  324. volcano_sdk/_generated/models/auth_refresh_body_session_mode.py +10 -0
  325. volcano_sdk/_generated/models/auth_request_email_change_body.py +76 -0
  326. volcano_sdk/_generated/models/auth_request_email_change_response_200.py +87 -0
  327. volcano_sdk/_generated/models/auth_resend_confirmation_body.py +76 -0
  328. volcano_sdk/_generated/models/auth_resend_confirmation_response_200.py +78 -0
  329. volcano_sdk/_generated/models/auth_reset_password_body.py +85 -0
  330. volcano_sdk/_generated/models/auth_reset_password_response_200.py +78 -0
  331. volcano_sdk/_generated/models/auth_session.py +239 -0
  332. volcano_sdk/_generated/models/auth_session_provider.py +10 -0
  333. volcano_sdk/_generated/models/auth_signin_body.py +108 -0
  334. volcano_sdk/_generated/models/auth_signin_body_session_mode.py +10 -0
  335. volcano_sdk/_generated/models/auth_signup_anonymous_body.py +94 -0
  336. volcano_sdk/_generated/models/auth_signup_anonymous_body_user_metadata.py +69 -0
  337. volcano_sdk/_generated/models/auth_signup_body.py +110 -0
  338. volcano_sdk/_generated/models/auth_signup_body_user_metadata.py +65 -0
  339. volcano_sdk/_generated/models/auth_signup_response.py +90 -0
  340. volcano_sdk/_generated/models/auth_token_response.py +119 -0
  341. volcano_sdk/_generated/models/auth_unlink_o_auth_provider_provider.py +10 -0
  342. volcano_sdk/_generated/models/auth_update_user_body.py +105 -0
  343. volcano_sdk/_generated/models/auth_update_user_body_user_metadata.py +68 -0
  344. volcano_sdk/_generated/models/auth_update_user_response_200.py +93 -0
  345. volcano_sdk/_generated/models/auth_user.py +276 -0
  346. volcano_sdk/_generated/models/auth_user_app_metadata.py +66 -0
  347. volcano_sdk/_generated/models/auth_user_status.py +10 -0
  348. volcano_sdk/_generated/models/auth_user_user_metadata.py +66 -0
  349. volcano_sdk/_generated/models/ban_auth_user_body.py +91 -0
  350. volcano_sdk/_generated/models/ban_user_response.py +146 -0
  351. volcano_sdk/_generated/models/ban_user_response_status.py +10 -0
  352. volcano_sdk/_generated/models/batch_function_deploy_failure.py +128 -0
  353. volcano_sdk/_generated/models/batch_function_deploy_failure_operation.py +10 -0
  354. volcano_sdk/_generated/models/batch_function_deploy_response.py +138 -0
  355. volcano_sdk/_generated/models/call_o_auth_provider_api_body.py +126 -0
  356. volcano_sdk/_generated/models/call_o_auth_provider_api_body_body.py +66 -0
  357. volcano_sdk/_generated/models/call_o_auth_provider_api_body_method.py +10 -0
  358. volcano_sdk/_generated/models/call_o_auth_provider_api_provider.py +10 -0
  359. volcano_sdk/_generated/models/call_o_auth_provider_api_response_200.py +107 -0
  360. volcano_sdk/_generated/models/call_o_auth_provider_api_response_200_provider.py +10 -0
  361. volcano_sdk/_generated/models/complete_upload_session_response.py +94 -0
  362. volcano_sdk/_generated/models/configure_auth_methods_body.py +118 -0
  363. volcano_sdk/_generated/models/configure_auth_methods_body_oauth_providers_item.py +87 -0
  364. volcano_sdk/_generated/models/connect_project_git_request.py +137 -0
  365. volcano_sdk/_generated/models/create_anon_key_body.py +112 -0
  366. volcano_sdk/_generated/models/create_anon_key_body_permissions_item.py +10 -0
  367. volcano_sdk/_generated/models/create_database_backup_request.py +78 -0
  368. volcano_sdk/_generated/models/create_database_branch_request.py +88 -0
  369. volcano_sdk/_generated/models/create_database_request.py +134 -0
  370. volcano_sdk/_generated/models/create_database_request_database_type.py +10 -0
  371. volcano_sdk/_generated/models/create_database_request_pg_version.py +10 -0
  372. volcano_sdk/_generated/models/create_database_request_region.py +10 -0
  373. volcano_sdk/_generated/models/create_database_restore_request.py +109 -0
  374. volcano_sdk/_generated/models/create_email_template_request.py +109 -0
  375. volcano_sdk/_generated/models/create_email_template_request_template_type.py +10 -0
  376. volcano_sdk/_generated/models/create_frontend_body.py +159 -0
  377. volcano_sdk/_generated/models/create_frontend_body_framework.py +10 -0
  378. volcano_sdk/_generated/models/create_frontend_custom_domain_request.py +92 -0
  379. volcano_sdk/_generated/models/create_function_body.py +243 -0
  380. volcano_sdk/_generated/models/create_function_body_runtime.py +10 -0
  381. volcano_sdk/_generated/models/create_function_scheduler_request.py +139 -0
  382. volcano_sdk/_generated/models/create_function_scheduler_request_payload.py +65 -0
  383. volcano_sdk/_generated/models/create_functions_batch_body.py +125 -0
  384. volcano_sdk/_generated/models/create_o_auth_config_request.py +128 -0
  385. volcano_sdk/_generated/models/create_o_auth_config_request_provider.py +10 -0
  386. volcano_sdk/_generated/models/create_project_request.py +109 -0
  387. volcano_sdk/_generated/models/create_service_key_body.py +100 -0
  388. volcano_sdk/_generated/models/create_storage_bucket_request.py +101 -0
  389. volcano_sdk/_generated/models/create_storage_policy_request.py +98 -0
  390. volcano_sdk/_generated/models/create_storage_policy_request_operation.py +10 -0
  391. volcano_sdk/_generated/models/create_upload_session_request.py +103 -0
  392. volcano_sdk/_generated/models/create_upload_session_response.py +119 -0
  393. volcano_sdk/_generated/models/create_variable_request.py +84 -0
  394. volcano_sdk/_generated/models/database.py +255 -0
  395. volcano_sdk/_generated/models/database_backup.py +142 -0
  396. volcano_sdk/_generated/models/database_backup_list.py +123 -0
  397. volcano_sdk/_generated/models/database_backup_schedule.py +96 -0
  398. volcano_sdk/_generated/models/database_backup_schedule_entry.py +114 -0
  399. volcano_sdk/_generated/models/database_backup_schedule_entry_frequency.py +10 -0
  400. volcano_sdk/_generated/models/database_backup_source.py +10 -0
  401. volcano_sdk/_generated/models/database_branch.py +235 -0
  402. volcano_sdk/_generated/models/database_branch_list.py +94 -0
  403. volcano_sdk/_generated/models/database_branch_status.py +10 -0
  404. volcano_sdk/_generated/models/database_branch_storage.py +99 -0
  405. volcano_sdk/_generated/models/database_database_type.py +10 -0
  406. volcano_sdk/_generated/models/database_delete_request.py +103 -0
  407. volcano_sdk/_generated/models/database_insert_request.py +93 -0
  408. volcano_sdk/_generated/models/database_insert_request_values.py +69 -0
  409. volcano_sdk/_generated/models/database_query_filter.py +142 -0
  410. volcano_sdk/_generated/models/database_query_filter_operator.py +10 -0
  411. volcano_sdk/_generated/models/database_query_order.py +96 -0
  412. volcano_sdk/_generated/models/database_query_performance_database.py +88 -0
  413. volcano_sdk/_generated/models/database_query_performance_item.py +156 -0
  414. volcano_sdk/_generated/models/database_query_performance_response.py +94 -0
  415. volcano_sdk/_generated/models/database_query_result.py +111 -0
  416. volcano_sdk/_generated/models/database_query_result_data_item.py +65 -0
  417. volcano_sdk/_generated/models/database_restore.py +221 -0
  418. volcano_sdk/_generated/models/database_restore_kind.py +10 -0
  419. volcano_sdk/_generated/models/database_restore_list.py +94 -0
  420. volcano_sdk/_generated/models/database_restore_status.py +10 -0
  421. volcano_sdk/_generated/models/database_restore_window.py +117 -0
  422. volcano_sdk/_generated/models/database_select_request.py +171 -0
  423. volcano_sdk/_generated/models/database_stats.py +211 -0
  424. volcano_sdk/_generated/models/database_stats_granularity.py +10 -0
  425. volcano_sdk/_generated/models/database_status.py +10 -0
  426. volcano_sdk/_generated/models/database_update_request.py +120 -0
  427. volcano_sdk/_generated/models/database_update_request_values.py +69 -0
  428. volcano_sdk/_generated/models/delete_database_backup_response_200.py +84 -0
  429. volcano_sdk/_generated/models/delete_database_branch_response_202.py +84 -0
  430. volcano_sdk/_generated/models/delete_database_response_202.py +87 -0
  431. volcano_sdk/_generated/models/delete_email_template_type.py +10 -0
  432. volcano_sdk/_generated/models/delete_o_auth_config_provider.py +10 -0
  433. volcano_sdk/_generated/models/deployment_phase.py +145 -0
  434. volcano_sdk/_generated/models/deployment_phase_name.py +10 -0
  435. volcano_sdk/_generated/models/deployment_phase_status.py +10 -0
  436. volcano_sdk/_generated/models/deployment_progress.py +168 -0
  437. volcano_sdk/_generated/models/deployment_progress_current_phase.py +10 -0
  438. volcano_sdk/_generated/models/deployment_reference.py +81 -0
  439. volcano_sdk/_generated/models/device_authorization_response.py +122 -0
  440. volcano_sdk/_generated/models/email_template.py +186 -0
  441. volcano_sdk/_generated/models/email_template_template_type.py +10 -0
  442. volcano_sdk/_generated/models/error.py +86 -0
  443. volcano_sdk/_generated/models/export_project_source_request.py +58 -0
  444. volcano_sdk/_generated/models/frontend.py +293 -0
  445. volcano_sdk/_generated/models/frontend_custom_domain_response.py +203 -0
  446. volcano_sdk/_generated/models/frontend_custom_domain_response_domain_status.py +10 -0
  447. volcano_sdk/_generated/models/frontend_custom_domain_response_tls_mode.py +10 -0
  448. volcano_sdk/_generated/models/frontend_custom_domain_response_verification_status.py +10 -0
  449. volcano_sdk/_generated/models/frontend_custom_domain_status.py +10 -0
  450. volcano_sdk/_generated/models/frontend_custom_domain_tls_config.py +108 -0
  451. volcano_sdk/_generated/models/frontend_custom_domain_tls_config_mode.py +10 -0
  452. volcano_sdk/_generated/models/frontend_deployment.py +304 -0
  453. volcano_sdk/_generated/models/frontend_deployment_deploy_source.py +10 -0
  454. volcano_sdk/_generated/models/frontend_deployment_operation.py +10 -0
  455. volcano_sdk/_generated/models/frontend_deployment_status.py +10 -0
  456. volcano_sdk/_generated/models/frontend_domain_routing_record.py +98 -0
  457. volcano_sdk/_generated/models/frontend_domain_routing_record_record_type.py +10 -0
  458. volcano_sdk/_generated/models/frontend_domain_verification_record.py +92 -0
  459. volcano_sdk/_generated/models/frontend_framework.py +10 -0
  460. volcano_sdk/_generated/models/frontend_status.py +10 -0
  461. volcano_sdk/_generated/models/frontend_usage_daily_entry.py +107 -0
  462. volcano_sdk/_generated/models/frontend_usage_data.py +112 -0
  463. volcano_sdk/_generated/models/frontend_usage_history_response.py +139 -0
  464. volcano_sdk/_generated/models/function.py +341 -0
  465. volcano_sdk/_generated/models/function_deployment.py +313 -0
  466. volcano_sdk/_generated/models/function_deployment_deploy_source.py +10 -0
  467. volcano_sdk/_generated/models/function_deployment_operation.py +10 -0
  468. volcano_sdk/_generated/models/function_deployment_status.py +10 -0
  469. volcano_sdk/_generated/models/function_http_auth_mode.py +10 -0
  470. volcano_sdk/_generated/models/function_invocation_mode.py +10 -0
  471. volcano_sdk/_generated/models/function_invocation_request.py +106 -0
  472. volcano_sdk/_generated/models/function_invocation_request_payload.py +79 -0
  473. volcano_sdk/_generated/models/function_invocation_response.py +66 -0
  474. volcano_sdk/_generated/models/function_openapi_spec_type_0.py +66 -0
  475. volcano_sdk/_generated/models/function_region.py +92 -0
  476. volcano_sdk/_generated/models/function_runtime_deployment.py +109 -0
  477. volcano_sdk/_generated/models/function_runtime_option.py +108 -0
  478. volcano_sdk/_generated/models/function_runtimes_response.py +94 -0
  479. volcano_sdk/_generated/models/function_scheduler.py +338 -0
  480. volcano_sdk/_generated/models/function_scheduler_list_response.py +146 -0
  481. volcano_sdk/_generated/models/function_scheduler_payload.py +65 -0
  482. volcano_sdk/_generated/models/function_scheduler_schedule_kind.py +10 -0
  483. volcano_sdk/_generated/models/function_status.py +10 -0
  484. volcano_sdk/_generated/models/get_auth_methods_response_200.py +158 -0
  485. volcano_sdk/_generated/models/get_auth_methods_response_200_anonymous.py +96 -0
  486. volcano_sdk/_generated/models/get_auth_methods_response_200_email_password.py +96 -0
  487. volcano_sdk/_generated/models/get_auth_methods_response_200_oauth_providers_item.py +129 -0
  488. volcano_sdk/_generated/models/get_database_stats_granularity.py +10 -0
  489. volcano_sdk/_generated/models/get_default_email_template_type.py +10 -0
  490. volcano_sdk/_generated/models/get_default_email_templates_response_200.py +100 -0
  491. volcano_sdk/_generated/models/get_email_template_type.py +10 -0
  492. volcano_sdk/_generated/models/get_o_auth_config_provider.py +10 -0
  493. volcano_sdk/_generated/models/get_o_auth_provider_token_provider.py +10 -0
  494. volcano_sdk/_generated/models/get_o_auth_provider_token_response_200.py +96 -0
  495. volcano_sdk/_generated/models/get_project_config_format.py +10 -0
  496. volcano_sdk/_generated/models/git_connect_start_response.py +76 -0
  497. volcano_sdk/_generated/models/git_connection.py +147 -0
  498. volcano_sdk/_generated/models/git_connections_response.py +94 -0
  499. volcano_sdk/_generated/models/git_installation.py +100 -0
  500. volcano_sdk/_generated/models/git_installations_response.py +94 -0
  501. volcano_sdk/_generated/models/git_repositories_response.py +94 -0
  502. volcano_sdk/_generated/models/git_repository.py +108 -0
  503. volcano_sdk/_generated/models/hosted_auth_page_type.py +10 -0
  504. volcano_sdk/_generated/models/hosted_login_email_check_request.py +76 -0
  505. volcano_sdk/_generated/models/hosted_login_email_check_response.py +78 -0
  506. volcano_sdk/_generated/models/hosted_login_options_response.py +120 -0
  507. volcano_sdk/_generated/models/hosted_renderable_page_type.py +10 -0
  508. volcano_sdk/_generated/models/import_connect_start_response.py +76 -0
  509. volcano_sdk/_generated/models/import_connection.py +205 -0
  510. volcano_sdk/_generated/models/import_connections_response.py +94 -0
  511. volcano_sdk/_generated/models/import_provider.py +10 -0
  512. volcano_sdk/_generated/models/import_source.py +100 -0
  513. volcano_sdk/_generated/models/import_sources_response.py +94 -0
  514. volcano_sdk/_generated/models/list_anon_keys_response_200.py +137 -0
  515. volcano_sdk/_generated/models/list_auth_users_status.py +10 -0
  516. volcano_sdk/_generated/models/list_available_o_auth_providers_response_200.py +100 -0
  517. volcano_sdk/_generated/models/list_available_o_auth_providers_response_200_providers_item.py +102 -0
  518. volcano_sdk/_generated/models/list_database_regions_response_200_item.py +87 -0
  519. volcano_sdk/_generated/models/list_databases_status.py +10 -0
  520. volcano_sdk/_generated/models/list_deployments_operation.py +10 -0
  521. volcano_sdk/_generated/models/list_deployments_order.py +10 -0
  522. volcano_sdk/_generated/models/list_deployments_resource_type.py +10 -0
  523. volcano_sdk/_generated/models/list_deployments_status.py +10 -0
  524. volcano_sdk/_generated/models/list_email_templates_response_200.py +100 -0
  525. volcano_sdk/_generated/models/list_o_auth_configs_response_200.py +100 -0
  526. volcano_sdk/_generated/models/list_postgres_versions_response_200_item.py +105 -0
  527. volcano_sdk/_generated/models/list_project_deployments_resource_type.py +10 -0
  528. volcano_sdk/_generated/models/list_projects_include_item.py +10 -0
  529. volcano_sdk/_generated/models/list_storage_objects_admin_response_200.py +155 -0
  530. volcano_sdk/_generated/models/list_user_sessions_response_200.py +190 -0
  531. volcano_sdk/_generated/models/list_user_sessions_sort.py +10 -0
  532. volcano_sdk/_generated/models/list_user_sessions_status.py +10 -0
  533. volcano_sdk/_generated/models/live_log_level.py +10 -0
  534. volcano_sdk/_generated/models/log_activity_bucket.py +116 -0
  535. volcano_sdk/_generated/models/log_activity_bucket_counts.py +113 -0
  536. volcano_sdk/_generated/models/log_activity_bucket_counts_levels.py +66 -0
  537. volcano_sdk/_generated/models/log_activity_bucket_counts_regions.py +66 -0
  538. volcano_sdk/_generated/models/log_activity_bucket_counts_resource_ids.py +66 -0
  539. volcano_sdk/_generated/models/log_activity_request.py +168 -0
  540. volcano_sdk/_generated/models/log_activity_response.py +103 -0
  541. volcano_sdk/_generated/models/log_database_request_resource.py +94 -0
  542. volcano_sdk/_generated/models/log_database_request_resource_type.py +10 -0
  543. volcano_sdk/_generated/models/log_deployment.py +105 -0
  544. volcano_sdk/_generated/models/log_deployment_request_selector.py +81 -0
  545. volcano_sdk/_generated/models/log_deployment_stage.py +10 -0
  546. volcano_sdk/_generated/models/log_event.py +222 -0
  547. volcano_sdk/_generated/models/log_event_body_type_1.py +65 -0
  548. volcano_sdk/_generated/models/log_frontend_request_resource.py +117 -0
  549. volcano_sdk/_generated/models/log_frontend_request_resource_type.py +10 -0
  550. volcano_sdk/_generated/models/log_function_request_resource.py +117 -0
  551. volcano_sdk/_generated/models/log_function_request_resource_type.py +10 -0
  552. volcano_sdk/_generated/models/log_resource.py +105 -0
  553. volcano_sdk/_generated/models/log_resource_type.py +10 -0
  554. volcano_sdk/_generated/models/log_search_event.py +212 -0
  555. volcano_sdk/_generated/models/log_search_request.py +177 -0
  556. volcano_sdk/_generated/models/log_search_response.py +121 -0
  557. volcano_sdk/_generated/models/log_stream_request.py +148 -0
  558. volcano_sdk/_generated/models/metric_usage_data.py +148 -0
  559. volcano_sdk/_generated/models/o_auth_config.py +201 -0
  560. volcano_sdk/_generated/models/o_auth_config_provider.py +10 -0
  561. volcano_sdk/_generated/models/o_auth_error_response.py +86 -0
  562. volcano_sdk/_generated/models/paginated_auth_users.py +136 -0
  563. volcano_sdk/_generated/models/paginated_databases.py +155 -0
  564. volcano_sdk/_generated/models/paginated_frontend_deployments.py +136 -0
  565. volcano_sdk/_generated/models/paginated_frontends.py +155 -0
  566. volcano_sdk/_generated/models/paginated_function_deployments.py +136 -0
  567. volcano_sdk/_generated/models/paginated_functions.py +155 -0
  568. volcano_sdk/_generated/models/paginated_project_custom_domains.py +136 -0
  569. volcano_sdk/_generated/models/paginated_project_deployments.py +159 -0
  570. volcano_sdk/_generated/models/paginated_projects.py +218 -0
  571. volcano_sdk/_generated/models/paginated_projects_status.py +10 -0
  572. volcano_sdk/_generated/models/paginated_service_keys.py +155 -0
  573. volcano_sdk/_generated/models/paginated_storage_buckets.py +140 -0
  574. volcano_sdk/_generated/models/paginated_variables.py +155 -0
  575. volcano_sdk/_generated/models/platform_exchange_response.py +109 -0
  576. volcano_sdk/_generated/models/preview_auth_page_request.py +89 -0
  577. volcano_sdk/_generated/models/preview_auth_page_response.py +89 -0
  578. volcano_sdk/_generated/models/project.py +255 -0
  579. volcano_sdk/_generated/models/project_config.py +288 -0
  580. volcano_sdk/_generated/models/project_config_apply_result.py +170 -0
  581. volcano_sdk/_generated/models/project_config_apply_result_entry.py +118 -0
  582. volcano_sdk/_generated/models/project_config_apply_result_entry_action.py +10 -0
  583. volcano_sdk/_generated/models/project_config_apply_summary.py +124 -0
  584. volcano_sdk/_generated/models/project_config_auth.py +296 -0
  585. volcano_sdk/_generated/models/project_config_auth_cors.py +93 -0
  586. volcano_sdk/_generated/models/project_config_auth_email.py +132 -0
  587. volcano_sdk/_generated/models/project_config_auth_email_from.py +69 -0
  588. volcano_sdk/_generated/models/project_config_auth_email_password_provider.py +60 -0
  589. volcano_sdk/_generated/models/project_config_auth_email_smtp.py +96 -0
  590. volcano_sdk/_generated/models/project_config_auth_email_verification.py +70 -0
  591. volcano_sdk/_generated/models/project_config_auth_managed_pages.py +129 -0
  592. volcano_sdk/_generated/models/project_config_auth_page_appearance.py +97 -0
  593. volcano_sdk/_generated/models/project_config_auth_page_layouts.py +174 -0
  594. volcano_sdk/_generated/models/project_config_auth_password.py +96 -0
  595. volcano_sdk/_generated/models/project_config_auth_password_reset.py +78 -0
  596. volcano_sdk/_generated/models/project_config_auth_providers.py +105 -0
  597. volcano_sdk/_generated/models/project_config_auth_rate_limits.py +88 -0
  598. volcano_sdk/_generated/models/project_config_auth_redirects.py +93 -0
  599. volcano_sdk/_generated/models/project_config_auth_sessions.py +69 -0
  600. volcano_sdk/_generated/models/project_config_auth_signup.py +118 -0
  601. volcano_sdk/_generated/models/project_config_auth_signup_allowed_email_domains_mode.py +10 -0
  602. volcano_sdk/_generated/models/project_config_auth_tokens.py +88 -0
  603. volcano_sdk/_generated/models/project_config_bucket.py +118 -0
  604. volcano_sdk/_generated/models/project_config_bucket_policy.py +80 -0
  605. volcano_sdk/_generated/models/project_config_bucket_policy_operation.py +10 -0
  606. volcano_sdk/_generated/models/project_config_custom_domain.py +88 -0
  607. volcano_sdk/_generated/models/project_config_database.py +109 -0
  608. volcano_sdk/_generated/models/project_config_database_database_type.py +10 -0
  609. volcano_sdk/_generated/models/project_config_database_pg_version.py +10 -0
  610. volcano_sdk/_generated/models/project_config_email_template.py +78 -0
  611. volcano_sdk/_generated/models/project_config_email_templates.py +136 -0
  612. volcano_sdk/_generated/models/project_config_frontend.py +91 -0
  613. volcano_sdk/_generated/models/project_config_function.py +188 -0
  614. volcano_sdk/_generated/models/project_config_function_openapi_spec_type_0.py +66 -0
  615. volcano_sdk/_generated/models/project_config_hosted_page.py +68 -0
  616. volcano_sdk/_generated/models/project_config_hosted_pages.py +172 -0
  617. volcano_sdk/_generated/models/project_config_missing_resource.py +90 -0
  618. volcano_sdk/_generated/models/project_config_missing_resource_type.py +10 -0
  619. volcano_sdk/_generated/models/project_config_o_auth_provider.py +116 -0
  620. volcano_sdk/_generated/models/project_config_o_auth_provider_provider.py +10 -0
  621. volcano_sdk/_generated/models/project_config_project.py +85 -0
  622. volcano_sdk/_generated/models/project_config_realtime.py +87 -0
  623. volcano_sdk/_generated/models/project_config_scheduler.py +100 -0
  624. volcano_sdk/_generated/models/project_config_scheduler_payload.py +65 -0
  625. volcano_sdk/_generated/models/project_config_skipped_resource.py +98 -0
  626. volcano_sdk/_generated/models/project_config_skipped_resource_type.py +10 -0
  627. volcano_sdk/_generated/models/project_config_validation_error.py +94 -0
  628. volcano_sdk/_generated/models/project_config_validation_error_response.py +103 -0
  629. volcano_sdk/_generated/models/project_config_variable.py +66 -0
  630. volcano_sdk/_generated/models/project_config_version.py +10 -0
  631. volcano_sdk/_generated/models/project_deployment.py +231 -0
  632. volcano_sdk/_generated/models/project_deployment_deploy_source.py +10 -0
  633. volcano_sdk/_generated/models/project_deployment_operation.py +10 -0
  634. volcano_sdk/_generated/models/project_deployment_resource.py +103 -0
  635. volcano_sdk/_generated/models/project_deployment_resource_type.py +10 -0
  636. volcano_sdk/_generated/models/project_deployment_status.py +10 -0
  637. volcano_sdk/_generated/models/project_deployment_summary.py +132 -0
  638. volcano_sdk/_generated/models/project_frontend_custom_domain.py +218 -0
  639. volcano_sdk/_generated/models/project_frontend_custom_domain_frontend.py +90 -0
  640. volcano_sdk/_generated/models/project_git_connection.py +122 -0
  641. volcano_sdk/_generated/models/project_git_connection_summary.py +121 -0
  642. volcano_sdk/_generated/models/project_git_deploy_settings.py +119 -0
  643. volcano_sdk/_generated/models/project_health_category.py +10 -0
  644. volcano_sdk/_generated/models/project_health_check.py +140 -0
  645. volcano_sdk/_generated/models/project_health_data_status.py +10 -0
  646. volcano_sdk/_generated/models/project_health_evidence.py +100 -0
  647. volcano_sdk/_generated/models/project_health_resource.py +102 -0
  648. volcano_sdk/_generated/models/project_health_resource_type.py +10 -0
  649. volcano_sdk/_generated/models/project_health_response.py +176 -0
  650. volcano_sdk/_generated/models/project_health_scope.py +84 -0
  651. volcano_sdk/_generated/models/project_health_status.py +10 -0
  652. volcano_sdk/_generated/models/project_health_summary.py +82 -0
  653. volcano_sdk/_generated/models/project_import_action.py +110 -0
  654. volcano_sdk/_generated/models/project_import_action_code.py +10 -0
  655. volcano_sdk/_generated/models/project_import_destination.py +103 -0
  656. volcano_sdk/_generated/models/project_import_destination_mode.py +10 -0
  657. volcano_sdk/_generated/models/project_import_disposition.py +10 -0
  658. volcano_sdk/_generated/models/project_import_finding.py +134 -0
  659. volcano_sdk/_generated/models/project_import_impact.py +10 -0
  660. volcano_sdk/_generated/models/project_import_preflight_request.py +123 -0
  661. volcano_sdk/_generated/models/project_import_readiness.py +10 -0
  662. volcano_sdk/_generated/models/project_import_report.py +230 -0
  663. volcano_sdk/_generated/models/project_import_resource.py +109 -0
  664. volcano_sdk/_generated/models/project_import_resource_kind.py +10 -0
  665. volcano_sdk/_generated/models/project_import_run.py +190 -0
  666. volcano_sdk/_generated/models/project_import_run_status.py +10 -0
  667. volcano_sdk/_generated/models/project_import_start_request.py +126 -0
  668. volcano_sdk/_generated/models/project_import_summary.py +116 -0
  669. volcano_sdk/_generated/models/project_import_target.py +10 -0
  670. volcano_sdk/_generated/models/project_lock_lease.py +75 -0
  671. volcano_sdk/_generated/models/project_lock_lease_request.py +62 -0
  672. volcano_sdk/_generated/models/project_lock_state.py +90 -0
  673. volcano_sdk/_generated/models/project_metrics_data_status.py +10 -0
  674. volcano_sdk/_generated/models/project_metrics_dimensions.py +83 -0
  675. volcano_sdk/_generated/models/project_metrics_dimensions_resource_type.py +10 -0
  676. volcano_sdk/_generated/models/project_metrics_group_by.py +10 -0
  677. volcano_sdk/_generated/models/project_metrics_metric.py +10 -0
  678. volcano_sdk/_generated/models/project_metrics_query.py +95 -0
  679. volcano_sdk/_generated/models/project_metrics_query_request.py +90 -0
  680. volcano_sdk/_generated/models/project_metrics_query_response.py +122 -0
  681. volcano_sdk/_generated/models/project_metrics_query_time_range.py +64 -0
  682. volcano_sdk/_generated/models/project_metrics_query_time_range_window.py +10 -0
  683. volcano_sdk/_generated/models/project_metrics_result.py +123 -0
  684. volcano_sdk/_generated/models/project_metrics_unit.py +10 -0
  685. volcano_sdk/_generated/models/project_metrics_value.py +74 -0
  686. volcano_sdk/_generated/models/project_metrics_window.py +92 -0
  687. volcano_sdk/_generated/models/project_plan.py +10 -0
  688. volcano_sdk/_generated/models/project_source_export.py +152 -0
  689. volcano_sdk/_generated/models/project_source_export_omission.py +93 -0
  690. volcano_sdk/_generated/models/project_source_export_skip.py +93 -0
  691. volcano_sdk/_generated/models/project_source_export_state.py +171 -0
  692. volcano_sdk/_generated/models/project_source_export_state_mode.py +10 -0
  693. volcano_sdk/_generated/models/project_status.py +10 -0
  694. volcano_sdk/_generated/models/project_usage_response.py +145 -0
  695. volcano_sdk/_generated/models/realtime_config.py +167 -0
  696. volcano_sdk/_generated/models/realtime_plan_limits.py +115 -0
  697. volcano_sdk/_generated/models/realtime_stats.py +132 -0
  698. volcano_sdk/_generated/models/refresh_o_auth_provider_token_provider.py +10 -0
  699. volcano_sdk/_generated/models/refresh_o_auth_provider_token_response_200.py +96 -0
  700. volcano_sdk/_generated/models/render_default_managed_auth_page_action.py +10 -0
  701. volcano_sdk/_generated/models/reset_database_password_response_200.py +106 -0
  702. volcano_sdk/_generated/models/resolve_function_response.py +96 -0
  703. volcano_sdk/_generated/models/resource_reference.py +103 -0
  704. volcano_sdk/_generated/models/resource_reference_type.py +10 -0
  705. volcano_sdk/_generated/models/schedule_request.py +101 -0
  706. volcano_sdk/_generated/models/schedule_request_kind.py +10 -0
  707. volcano_sdk/_generated/models/service_key.py +183 -0
  708. volcano_sdk/_generated/models/set_project_git_production_branch_request.py +77 -0
  709. volcano_sdk/_generated/models/start_git_connect_provider.py +10 -0
  710. volcano_sdk/_generated/models/storage_bucket.py +223 -0
  711. volcano_sdk/_generated/models/storage_copy_request.py +84 -0
  712. volcano_sdk/_generated/models/storage_list_response.py +109 -0
  713. volcano_sdk/_generated/models/storage_move_request.py +84 -0
  714. volcano_sdk/_generated/models/storage_object.py +243 -0
  715. volcano_sdk/_generated/models/storage_object_metadata.py +67 -0
  716. volcano_sdk/_generated/models/storage_object_with_bucket.py +248 -0
  717. volcano_sdk/_generated/models/storage_object_with_bucket_metadata.py +65 -0
  718. volcano_sdk/_generated/models/storage_policy.py +166 -0
  719. volcano_sdk/_generated/models/storage_policy_operation.py +10 -0
  720. volcano_sdk/_generated/models/storage_stats.py +93 -0
  721. volcano_sdk/_generated/models/storage_visibility_request.py +76 -0
  722. volcano_sdk/_generated/models/summarize_project_deployments_resource_type.py +10 -0
  723. volcano_sdk/_generated/models/test_email_request.py +115 -0
  724. volcano_sdk/_generated/models/test_email_response.py +76 -0
  725. volcano_sdk/_generated/models/unban_user_response.py +111 -0
  726. volcano_sdk/_generated/models/unban_user_response_status.py +10 -0
  727. volcano_sdk/_generated/models/update_auth_config_request.py +485 -0
  728. volcano_sdk/_generated/models/update_auth_config_request_allowed_email_domains_mode.py +10 -0
  729. volcano_sdk/_generated/models/update_auth_hosted_page_request.py +86 -0
  730. volcano_sdk/_generated/models/update_auth_page_layout_request.py +64 -0
  731. volcano_sdk/_generated/models/update_auth_page_theme_request.py +66 -0
  732. volcano_sdk/_generated/models/update_database_branch_request.py +77 -0
  733. volcano_sdk/_generated/models/update_database_type_request.py +83 -0
  734. volcano_sdk/_generated/models/update_database_type_request_database_type.py +10 -0
  735. volcano_sdk/_generated/models/update_email_template_request.py +96 -0
  736. volcano_sdk/_generated/models/update_email_template_type.py +10 -0
  737. volcano_sdk/_generated/models/update_function_request.py +149 -0
  738. volcano_sdk/_generated/models/update_function_request_openapi_spec_type_0.py +66 -0
  739. volcano_sdk/_generated/models/update_function_scheduler_request.py +147 -0
  740. volcano_sdk/_generated/models/update_function_scheduler_request_payload.py +65 -0
  741. volcano_sdk/_generated/models/update_o_auth_config_provider.py +10 -0
  742. volcano_sdk/_generated/models/update_o_auth_config_request.py +120 -0
  743. volcano_sdk/_generated/models/update_project_git_deploy_settings_request.py +105 -0
  744. volcano_sdk/_generated/models/update_project_request.py +107 -0
  745. volcano_sdk/_generated/models/update_realtime_config_request.py +107 -0
  746. volcano_sdk/_generated/models/update_storage_bucket_request.py +93 -0
  747. volcano_sdk/_generated/models/update_variable_request.py +76 -0
  748. volcano_sdk/_generated/models/upload_project_logo_body.py +101 -0
  749. volcano_sdk/_generated/models/upload_session_part.py +97 -0
  750. volcano_sdk/_generated/models/upload_session_status_response.py +234 -0
  751. volcano_sdk/_generated/models/upload_session_status_response_status.py +10 -0
  752. volcano_sdk/_generated/models/upload_storage_object_files_body.py +101 -0
  753. volcano_sdk/_generated/models/upload_storage_object_x_upload_complete.py +10 -0
  754. volcano_sdk/_generated/models/usage_data_point.py +90 -0
  755. volcano_sdk/_generated/models/variable.py +216 -0
  756. volcano_sdk/_generated/models/variable_deploy_source.py +10 -0
  757. volcano_sdk/_generated/models/variable_status.py +10 -0
  758. volcano_sdk/_generated/types.py +53 -0
  759. volcano_sdk/_lock_guard.py +159 -0
  760. volcano_sdk/_lock_renewer.py +30 -0
  761. volcano_sdk/_lock_worker.py +104 -0
  762. volcano_sdk/_realtime_fetch_worker.py +265 -0
  763. volcano_sdk/_transport.py +1767 -0
  764. volcano_sdk/auth.py +1128 -0
  765. volcano_sdk/client.py +288 -0
  766. volcano_sdk/connection_string.py +70 -0
  767. volcano_sdk/database.py +281 -0
  768. volcano_sdk/errors.py +61 -0
  769. volcano_sdk/functions.py +277 -0
  770. volcano_sdk/locks.py +227 -0
  771. volcano_sdk/logs.py +140 -0
  772. volcano_sdk/models.py +330 -0
  773. volcano_sdk/realtime.py +1611 -0
  774. volcano_sdk/storage.py +750 -0
  775. volcano_sdk_python-0.6.7.dist-info/METADATA +828 -0
  776. volcano_sdk_python-0.6.7.dist-info/RECORD +778 -0
  777. volcano_sdk_python-0.6.7.dist-info/WHEEL +4 -0
  778. volcano_sdk_python-0.6.7.dist-info/licenses/LICENSE +203 -0
@@ -0,0 +1,1611 @@
1
+ """Realtime broadcast facade."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import importlib
7
+ import inspect
8
+ from collections.abc import Awaitable, Callable, Mapping
9
+ from dataclasses import dataclass, field, replace
10
+ from types import MappingProxyType
11
+ from typing import TYPE_CHECKING, Any, Literal, Protocol, TypeAlias, cast
12
+ from urllib.parse import quote, urlsplit, urlunsplit
13
+
14
+ from ._realtime_fetch_worker import (
15
+ PostgresFetchJob,
16
+ PostgresFetchOutcome,
17
+ PostgresFetchWorker,
18
+ )
19
+ from ._transport import (
20
+ AsyncDatabaseSelectTransport,
21
+ Transport,
22
+ invoke_async,
23
+ response_payload,
24
+ )
25
+ from .models import JSONValue, _freeze_json
26
+
27
+ if TYPE_CHECKING:
28
+ from .models import Session
29
+
30
+ MessageCallback = Callable[[Any], Any]
31
+ RealtimeCallback = Callable[[Any], Any]
32
+ UnsubscribeCallback = Callable[[], None]
33
+ ChannelType: TypeAlias = Literal["broadcast", "presence", "postgres"]
34
+ PostgresEvent: TypeAlias = Literal["INSERT", "UPDATE", "DELETE"]
35
+ PostgresListenerEvent: TypeAlias = Literal["INSERT", "UPDATE", "DELETE", "*"]
36
+ PostgresChangeCallback = Callable[["PostgresChange"], Any]
37
+ SUPPORTED_CHANNEL_TYPES = frozenset({"broadcast", "presence", "postgres"})
38
+ POSTGRES_EVENTS = frozenset({"INSERT", "UPDATE", "DELETE"})
39
+ POSTGRES_CHANNEL_SEGMENTS = 3
40
+ POSTGRES_PUBLICATION_SEGMENTS = 5
41
+ CENTRIFUGE_ERROR = cast(
42
+ "type[Exception]",
43
+ importlib.import_module("centrifuge").CentrifugeError,
44
+ )
45
+ CALLBACK_QUEUE_LIMIT = 128
46
+ POSTGRES_QUEUE_LIMIT = 128
47
+ POSTGRES_BATCH_WINDOW_MS = 20
48
+ POSTGRES_MAX_BATCH_SIZE = 50
49
+ NO_PENDING_CALLBACK = object()
50
+ CALLBACK_QUEUE_FULL_MESSAGE = (
51
+ "Volcano realtime callback queue is full; publication dropped"
52
+ )
53
+ CHANNEL_NOT_SUBSCRIBED = "Channel must be subscribed before sending"
54
+ CHANNEL_REMOVAL_IN_PROGRESS = "realtime channel removal is in progress"
55
+ CHANNEL_NOT_MANAGED = "realtime channel is no longer managed"
56
+ PRESENCE_ONLY = "operation is only available for presence channels"
57
+ BROADCAST_ONLY = "send is only available for broadcast channels"
58
+ POSTGRES_ONLY = "operation is only available for postgres channels"
59
+ CALLBACK_NOT_CALLABLE = "callback must be callable"
60
+ SUBSCRIPTION_REGISTRY_UNAVAILABLE = (
61
+ "centrifuge client subscription registry is unavailable"
62
+ )
63
+ NO_ACTIVE_SESSION = "No active session"
64
+ CONNECTION_SESSION_UNAVAILABLE = "Realtime connection has no session binding"
65
+ CONNECTION_SESSION_CHANGED = "Realtime connection session changed"
66
+ POSTGRES_FETCH_FAILED_MESSAGE = "Volcano realtime Postgres row fetch failed"
67
+
68
+
69
+ def _empty_presence_data() -> Mapping[str, JSONValue]:
70
+ return MappingProxyType({})
71
+
72
+
73
+ def _consume_presence_result(task: asyncio.Task[Any]) -> None:
74
+ if not task.cancelled():
75
+ task.exception()
76
+
77
+
78
+ def _validate_channel_type(channel_type: str) -> ChannelType:
79
+ if channel_type not in SUPPORTED_CHANNEL_TYPES:
80
+ message = f"unsupported realtime channel type: {channel_type}"
81
+ raise ValueError(message)
82
+ return cast("ChannelType", channel_type)
83
+
84
+
85
+ def _postgres_route_matches(candidate: str, publication: str) -> bool:
86
+ candidate_parts = candidate.split(":")
87
+ publication_parts = publication.split(":")
88
+ return (
89
+ len(candidate_parts) == POSTGRES_CHANNEL_SEGMENTS
90
+ and candidate_parts[0] == "postgres"
91
+ and len(publication_parts) == POSTGRES_PUBLICATION_SEGMENTS
92
+ and publication_parts[1:4] == candidate_parts
93
+ )
94
+
95
+
96
+ @dataclass(frozen=True, slots=True)
97
+ class RealtimeConnectContext:
98
+ """Details reported after a realtime transport connects."""
99
+
100
+ client: str | None = None
101
+
102
+
103
+ @dataclass(frozen=True, slots=True)
104
+ class RealtimeDisconnectContext:
105
+ """Details reported after a realtime transport disconnects."""
106
+
107
+ code: int | None = None
108
+ reason: str | None = None
109
+
110
+
111
+ @dataclass(frozen=True, slots=True)
112
+ class RealtimeErrorContext:
113
+ """Details reported when the realtime transport emits an error."""
114
+
115
+ code: int | None = None
116
+ message: str | None = None
117
+ error: Exception | None = None
118
+
119
+
120
+ @dataclass(frozen=True, slots=True)
121
+ class RealtimePresenceInfo:
122
+ """Immutable identity and metadata for one present realtime client."""
123
+
124
+ client: str
125
+ user: str | None = None
126
+ data: Mapping[str, JSONValue] = field(
127
+ default_factory=_empty_presence_data,
128
+ hash=False,
129
+ )
130
+
131
+ def __post_init__(self) -> None:
132
+ """Defensively freeze nested connection metadata."""
133
+ frozen = _freeze_json(cast("JSONValue", dict(self.data)))
134
+ object.__setattr__(self, "data", cast("Mapping[str, JSONValue]", frozen))
135
+
136
+
137
+ @dataclass(frozen=True, slots=True)
138
+ class PostgresChange:
139
+ """Immutable RLS-scoped Postgres row-change notification."""
140
+
141
+ type: PostgresEvent
142
+ schema: str
143
+ table: str
144
+ record: Mapping[str, JSONValue] | None = field(default=None, hash=False)
145
+ old_record: Mapping[str, JSONValue] | None = field(default=None, hash=False)
146
+ columns: tuple[str, ...] | None = None
147
+ timestamp: str = ""
148
+ id: JSONValue = field(default=None, hash=False)
149
+ mode: Literal["lightweight"] | None = None
150
+
151
+ def __post_init__(self) -> None:
152
+ """Defensively freeze nested row and identifier values."""
153
+ if self.record is not None:
154
+ frozen_record = _freeze_json(cast("JSONValue", dict(self.record)))
155
+ object.__setattr__(self, "record", frozen_record)
156
+ if self.old_record is not None:
157
+ frozen_old_record = _freeze_json(cast("JSONValue", dict(self.old_record)))
158
+ object.__setattr__(self, "old_record", frozen_old_record)
159
+ object.__setattr__(self, "id", _freeze_json(self.id))
160
+
161
+
162
+ @dataclass(frozen=True, slots=True)
163
+ class _PostgresFetchRequest:
164
+ database_name: str
165
+ access_token: str
166
+ table: str
167
+ row_id: JSONValue
168
+
169
+
170
+ @dataclass(frozen=True, slots=True)
171
+ class _PostgresFetchConfig:
172
+ enabled: bool
173
+ batch_window_ms: int
174
+ max_batch_size: int
175
+
176
+ @property
177
+ def batch_window_seconds(self) -> float:
178
+ return self.batch_window_ms / 1_000
179
+
180
+
181
+ def _postgres_fetch_config(
182
+ *,
183
+ auto_fetch: bool,
184
+ fetch_batch_window_ms: object,
185
+ fetch_max_batch_size: object,
186
+ ) -> _PostgresFetchConfig:
187
+ if type(fetch_batch_window_ms) is not int or fetch_batch_window_ms <= 0:
188
+ message = "fetch_batch_window_ms must be a positive integer"
189
+ raise ValueError(message)
190
+ if (
191
+ type(fetch_max_batch_size) is not int
192
+ or not 1 <= fetch_max_batch_size <= POSTGRES_QUEUE_LIMIT
193
+ ):
194
+ message = (
195
+ f"fetch_max_batch_size must be an integer between 1 and "
196
+ f"{POSTGRES_QUEUE_LIMIT}"
197
+ )
198
+ raise ValueError(message)
199
+ return _PostgresFetchConfig(
200
+ enabled=auto_fetch,
201
+ batch_window_ms=fetch_batch_window_ms,
202
+ max_batch_size=fetch_max_batch_size,
203
+ )
204
+
205
+
206
+ @dataclass(frozen=True, slots=True)
207
+ class _PostgresDeliveryIdentity:
208
+ session_lineage: int
209
+ subscription_epoch: int
210
+
211
+
212
+ @dataclass(frozen=True, slots=True)
213
+ class _PostgresDelivery:
214
+ change: PostgresChange
215
+ identity: _PostgresDeliveryIdentity
216
+
217
+
218
+ @dataclass(frozen=True, slots=True)
219
+ class _CallbackDelivery:
220
+ event: str
221
+ data: Any
222
+ postgres_identity: _PostgresDeliveryIdentity | None = None
223
+ delivery_epoch: int | None = None
224
+
225
+
226
+ def _postgres_change(data: Any) -> PostgresChange | None:
227
+ if not isinstance(data, Mapping):
228
+ return None
229
+ typed_data = cast("Mapping[str, object]", data)
230
+ event = typed_data.get("type")
231
+ schema = typed_data.get("schema")
232
+ table = typed_data.get("table")
233
+ timestamp = typed_data.get("timestamp")
234
+ mode = typed_data.get("mode")
235
+ record = typed_data.get("record")
236
+ old_record = typed_data.get("old_record")
237
+ raw_columns = typed_data.get("columns")
238
+ if (
239
+ event not in POSTGRES_EVENTS
240
+ or not isinstance(schema, str)
241
+ or not isinstance(table, str)
242
+ or not isinstance(timestamp, str)
243
+ or (record is not None and not isinstance(record, Mapping))
244
+ or (old_record is not None and not isinstance(old_record, Mapping))
245
+ ):
246
+ return None
247
+ typed_mode: Literal["lightweight"] | None
248
+ if mode is None:
249
+ typed_mode = None
250
+ elif mode == "lightweight":
251
+ typed_mode = "lightweight"
252
+ else:
253
+ return None
254
+ if raw_columns is None:
255
+ columns = None
256
+ elif isinstance(raw_columns, (list, tuple)):
257
+ untyped_columns = cast("list[object] | tuple[object, ...]", raw_columns)
258
+ if not all(isinstance(column, str) for column in untyped_columns):
259
+ return None
260
+ columns = tuple(cast("list[str] | tuple[str, ...]", raw_columns))
261
+ else:
262
+ return None
263
+ return PostgresChange(
264
+ type=cast("PostgresEvent", event),
265
+ schema=schema,
266
+ table=table,
267
+ record=cast("Mapping[str, JSONValue] | None", record),
268
+ old_record=cast("Mapping[str, JSONValue] | None", old_record),
269
+ columns=columns,
270
+ timestamp=timestamp,
271
+ id=cast("JSONValue", typed_data.get("id")),
272
+ mode=typed_mode,
273
+ )
274
+
275
+
276
+ class RealtimeContext(Protocol):
277
+ """Client capabilities required by realtime connections."""
278
+
279
+ _transport: Transport
280
+
281
+ def _anon_token(self) -> str: ...
282
+
283
+ def _session_token(self) -> str: ...
284
+
285
+ def _capture_session_binding(self) -> tuple[int, int, Session | None]: ...
286
+
287
+
288
+ class CentrifugeSubscription(Protocol):
289
+ """Centrifuge subscription operations used by the SDK."""
290
+
291
+ async def subscribe(self) -> None:
292
+ """Subscribe to the remote channel."""
293
+ ...
294
+
295
+ async def ready(self) -> None:
296
+ """Wait for acknowledgement using the client request timeout."""
297
+ ...
298
+
299
+ async def publish(self, data: Any) -> Any:
300
+ """Publish a payload to the remote channel."""
301
+ ...
302
+
303
+ async def unsubscribe(self) -> None:
304
+ """Unsubscribe from the remote channel."""
305
+ ...
306
+
307
+ async def presence(self) -> Any:
308
+ """Return the clients currently present on the channel."""
309
+ ...
310
+
311
+
312
+ async def _unsubscribe_native(subscription: CentrifugeSubscription) -> None:
313
+ # Finish the native stop before releasing the connection lock on cancellation.
314
+ task = asyncio.create_task(subscription.unsubscribe())
315
+ cancelled: asyncio.CancelledError | None = None
316
+ while not task.done():
317
+ try:
318
+ await asyncio.shield(task)
319
+ except asyncio.CancelledError as error:
320
+ cancelled = error
321
+ except CENTRIFUGE_ERROR:
322
+ if cancelled is None:
323
+ raise
324
+ if cancelled is not None:
325
+ if not task.cancelled():
326
+ task.exception()
327
+ raise cancelled
328
+ task.result()
329
+
330
+
331
+ class CentrifugeConnection(Protocol):
332
+ """Centrifuge connection operations used by the SDK."""
333
+
334
+ state: Any
335
+
336
+ async def connect(self) -> None:
337
+ """Open the remote connection."""
338
+ ...
339
+
340
+ async def disconnect(self) -> None:
341
+ """Close the remote connection."""
342
+ ...
343
+
344
+ def new_subscription(
345
+ self,
346
+ name: str,
347
+ *,
348
+ events: Any,
349
+ join_leave: bool = False,
350
+ recoverable: bool = False,
351
+ ) -> CentrifugeSubscription:
352
+ """Create a subscription for a remote channel."""
353
+ ...
354
+
355
+ def remove_subscription(self, subscription: Any) -> None:
356
+ """Remove an unsubscribed channel from the connection registry."""
357
+ ...
358
+
359
+
360
+ class CentrifugeFactory(Protocol):
361
+ """Construct a typed Centrifuge connection."""
362
+
363
+ def __call__(
364
+ self,
365
+ address: str,
366
+ *,
367
+ events: Any,
368
+ token: str,
369
+ get_token: Callable[[], Awaitable[str]],
370
+ ) -> CentrifugeConnection:
371
+ """Construct a Centrifuge connection."""
372
+ ...
373
+
374
+
375
+ class CentrifugeConstructor(Protocol):
376
+ """Describe the dynamically imported Centrifuge client constructor."""
377
+
378
+ def __call__(
379
+ self,
380
+ address: str,
381
+ *,
382
+ events: Any,
383
+ token: str,
384
+ get_token: Callable[[], Awaitable[str]],
385
+ ) -> object:
386
+ """Construct the dynamically imported Centrifuge client."""
387
+ ...
388
+
389
+
390
+ class Publication(Protocol):
391
+ """Publication payload received from Centrifuge."""
392
+
393
+ data: Any
394
+
395
+
396
+ class PublicationContext(Protocol):
397
+ """Centrifuge callback context containing a publication."""
398
+
399
+ pub: Publication
400
+
401
+
402
+ def _centrifuge_client(
403
+ address: str,
404
+ *,
405
+ events: Any,
406
+ token: str,
407
+ get_token: Callable[[], Awaitable[str]],
408
+ ) -> CentrifugeConnection:
409
+ module = importlib.import_module("centrifuge")
410
+ constructor = cast("CentrifugeConstructor", module.Client)
411
+ return cast(
412
+ "CentrifugeConnection",
413
+ constructor(address, events=events, token=token, get_token=get_token),
414
+ )
415
+
416
+
417
+ class _ProjectAwareSubscriptions(dict[str, Any]):
418
+ def get(self, key: str, default: Any = None) -> Any:
419
+ subscription = super().get(key)
420
+ if subscription is not None:
421
+ return subscription
422
+ matches = [
423
+ (channel, candidate)
424
+ for channel, candidate in self.items()
425
+ if key.endswith(f":{channel}") or _postgres_route_matches(channel, key)
426
+ ]
427
+ return max(matches, key=lambda match: len(match[0]))[1] if matches else default
428
+
429
+
430
+ class _VolcanoCentrifugeConnection:
431
+ def __init__(self, connection: CentrifugeConnection) -> None:
432
+ self._connection = connection
433
+ state = vars(connection)
434
+ subscriptions = state.get("_subs")
435
+ if not isinstance(subscriptions, dict):
436
+ raise TypeError(SUBSCRIPTION_REGISTRY_UNAVAILABLE)
437
+ typed_subscriptions = cast("dict[str, Any]", subscriptions)
438
+ state["_subs"] = _ProjectAwareSubscriptions(typed_subscriptions)
439
+
440
+ async def connect(self) -> None:
441
+ await self._connection.connect()
442
+
443
+ async def disconnect(self) -> None:
444
+ await self._connection.disconnect()
445
+
446
+ @property
447
+ def is_connected(self) -> bool:
448
+ state = self._connection.state
449
+ return getattr(state, "value", None) == "connected"
450
+
451
+ def new_subscription(
452
+ self,
453
+ name: str,
454
+ *,
455
+ events: Any,
456
+ join_leave: bool = False,
457
+ recoverable: bool = False,
458
+ ) -> CentrifugeSubscription:
459
+ return self._connection.new_subscription(
460
+ name,
461
+ events=events,
462
+ join_leave=join_leave,
463
+ recoverable=recoverable,
464
+ )
465
+
466
+ def remove_subscription(self, subscription: CentrifugeSubscription) -> None:
467
+ self._connection.remove_subscription(subscription)
468
+
469
+
470
+ class _ChannelEvents:
471
+ def __init__(self, channel: Channel) -> None:
472
+ self._channel = channel
473
+
474
+ def _is_current(self) -> bool:
475
+ return self._channel._subscription_events is self
476
+
477
+ async def on_publication(self, ctx: PublicationContext) -> None:
478
+ if (
479
+ not self._is_current()
480
+ or self._channel._paused
481
+ or not self._channel._subscribed
482
+ ):
483
+ return
484
+ if self._channel._type == "postgres":
485
+ await self._channel._receive_postgres_change(ctx.pub.data)
486
+ return
487
+ await self._channel._emit("message", ctx.pub.data)
488
+
489
+ async def on_subscribing(self, ctx: Any) -> None:
490
+ del ctx
491
+ if self._is_current():
492
+ await self._channel._transport_lost()
493
+
494
+ async def on_subscribed(self, ctx: Any) -> None:
495
+ del ctx
496
+ if not self._is_current() or self._channel._paused:
497
+ return
498
+ self._channel._subscribed = True
499
+ await self._channel._begin_postgres_epoch()
500
+ if self._channel._type == "presence":
501
+ self._channel._schedule_presence_sync()
502
+
503
+ async def on_unsubscribed(self, ctx: Any) -> None:
504
+ del ctx
505
+ if self._is_current():
506
+ await self._channel._transport_lost()
507
+
508
+ async def on_join(self, ctx: Any) -> None:
509
+ if self._is_current():
510
+ await self._channel._presence_join(getattr(ctx, "info", None))
511
+
512
+ async def on_leave(self, ctx: Any) -> None:
513
+ if self._is_current():
514
+ await self._channel._presence_leave(getattr(ctx, "info", None))
515
+
516
+ async def on_error(self, ctx: Any) -> None:
517
+ del ctx
518
+
519
+
520
+ class _ClientEvents:
521
+ def __init__(self, realtime: Realtime) -> None:
522
+ self._realtime = realtime
523
+
524
+ async def on_connecting(self, ctx: Any) -> None:
525
+ del ctx
526
+
527
+ async def on_connected(self, ctx: Any) -> None:
528
+ self._realtime._enqueue_connection_callbacks(
529
+ "connect",
530
+ RealtimeConnectContext(client=getattr(ctx, "client", None)),
531
+ )
532
+
533
+ async def on_disconnected(self, ctx: Any) -> None:
534
+ self._realtime._enqueue_connection_callbacks(
535
+ "disconnect",
536
+ RealtimeDisconnectContext(
537
+ code=getattr(ctx, "code", None),
538
+ reason=getattr(ctx, "reason", None),
539
+ ),
540
+ )
541
+
542
+ async def on_error(self, ctx: Any) -> None:
543
+ error = getattr(ctx, "error", None)
544
+ self._realtime._enqueue_connection_callbacks(
545
+ "error",
546
+ RealtimeErrorContext(
547
+ code=getattr(ctx, "code", None),
548
+ message=str(error) if error is not None else None,
549
+ error=error if isinstance(error, Exception) else None,
550
+ ),
551
+ )
552
+
553
+ async def on_subscribed(self, ctx: Any) -> None:
554
+ del ctx
555
+
556
+ async def on_subscribing(self, ctx: Any) -> None:
557
+ del ctx
558
+
559
+ async def on_unsubscribed(self, ctx: Any) -> None:
560
+ del ctx
561
+
562
+ async def on_publication(self, ctx: Any) -> None:
563
+ del ctx
564
+
565
+ async def on_join(self, ctx: Any) -> None:
566
+ del ctx
567
+
568
+ async def on_leave(self, ctx: Any) -> None:
569
+ del ctx
570
+
571
+
572
+ class Channel:
573
+ """Realtime broadcast, presence, or Postgres channel."""
574
+
575
+ def __init__(
576
+ self,
577
+ realtime: Realtime,
578
+ name: str,
579
+ channel_type: ChannelType,
580
+ *,
581
+ fetch_config: _PostgresFetchConfig,
582
+ ) -> None:
583
+ """Create a channel managed by a realtime facade."""
584
+ self._realtime = realtime
585
+ self._name = name
586
+ self._type = channel_type
587
+ self._fetch_config = fetch_config
588
+ self._callbacks: dict[str, list[MessageCallback]] = {}
589
+ self._presence_state: dict[str, RealtimePresenceInfo] = {}
590
+ self._presence_events: list[tuple[str, RealtimePresenceInfo]] = []
591
+ self._presence_syncing = False
592
+ self._tracked_state: Mapping[str, JSONValue] = MappingProxyType({})
593
+ self._subscribe_lock = asyncio.Lock()
594
+ self._subscribe_generation = 0
595
+ self._readiness_task: asyncio.Task[None] | None = None
596
+ self._subscription: CentrifugeSubscription | None = None
597
+ self._subscription_events: _ChannelEvents | None = None
598
+ self._subscribed = False
599
+ self._paused = True
600
+ self._delivery_epoch = 0
601
+ self._presence_epoch = 0
602
+ self._presence_lock = asyncio.Lock()
603
+ self._presence_sync_task: asyncio.Task[None] | None = None
604
+ self._presence_sync_pending = False
605
+ self._callback_queue: asyncio.Queue[_CallbackDelivery] = asyncio.Queue(
606
+ maxsize=CALLBACK_QUEUE_LIMIT
607
+ )
608
+ self._callback_task: asyncio.Task[None] | None = None
609
+ self._pending_presence_sync: Any = NO_PENDING_CALLBACK
610
+ self._postgres_epoch = 0
611
+ self._postgres_session_lineage = 0
612
+ self._postgres_lock = asyncio.Lock()
613
+ self._postgres_worker: PostgresFetchWorker[_PostgresDelivery] | None = None
614
+ self._postgres_filters: dict[
615
+ int,
616
+ tuple[PostgresListenerEvent, str, str],
617
+ ] = {}
618
+
619
+ @property
620
+ def name(self) -> str:
621
+ """Return the canonical channel name sent to realtime."""
622
+ return self._name
623
+
624
+ def on(self, event: str, callback: MessageCallback) -> Channel:
625
+ """Register a callback for messages or presence events."""
626
+ allowed_events = {
627
+ "broadcast": {"message"},
628
+ "presence": {"message", "join", "leave", "presence_sync"},
629
+ "postgres": {"*"},
630
+ }[self._type]
631
+ if event not in allowed_events:
632
+ message = f"unsupported realtime event: {event}"
633
+ raise ValueError(message)
634
+ self._callbacks.setdefault(event, []).append(callback)
635
+ return self
636
+
637
+ def on_postgres_changes(
638
+ self,
639
+ event: PostgresListenerEvent,
640
+ *,
641
+ schema: str,
642
+ table: str,
643
+ callback: PostgresChangeCallback,
644
+ ) -> UnsubscribeCallback:
645
+ """Observe Postgres changes filtered by event, schema, and table."""
646
+ if self._type != "postgres":
647
+ raise ValueError(POSTGRES_ONLY)
648
+ if event not in {*POSTGRES_EVENTS, "*"}:
649
+ message = f"unsupported Postgres change event: {event}"
650
+ raise ValueError(message)
651
+
652
+ def filtered(change: PostgresChange) -> Any:
653
+ if change.schema != schema or change.table != table:
654
+ return None
655
+ if event not in ("*", change.type):
656
+ return None
657
+ return callback(change)
658
+
659
+ self._callbacks.setdefault("*", []).append(filtered)
660
+ self._postgres_filters[id(filtered)] = (event, schema, table)
661
+
662
+ def unsubscribe() -> None:
663
+ callbacks = self._callbacks.get("*", [])
664
+ if filtered in callbacks:
665
+ callbacks.remove(filtered)
666
+ self._postgres_filters.pop(id(filtered), None)
667
+
668
+ return unsubscribe
669
+
670
+ def on_presence_sync(self, callback: MessageCallback) -> UnsubscribeCallback:
671
+ """Observe immutable snapshots of a presence channel's current state."""
672
+ self._ensure_presence()
673
+ self._callbacks.setdefault("presence_sync", []).append(callback)
674
+
675
+ def unsubscribe() -> None:
676
+ callbacks = self._callbacks.get("presence_sync", [])
677
+ if callback in callbacks:
678
+ callbacks.remove(callback)
679
+
680
+ return unsubscribe
681
+
682
+ async def track(self, state: Mapping[str, JSONValue] | None = None) -> None:
683
+ """Store local presence state while server identity remains authoritative."""
684
+ self._ensure_presence()
685
+ if not self._subscribed:
686
+ raise RuntimeError(CHANNEL_NOT_SUBSCRIBED)
687
+ frozen = _freeze_json(cast("JSONValue", dict(state or {})))
688
+ self._tracked_state = cast("Mapping[str, JSONValue]", frozen)
689
+
690
+ def get_presence_state(self) -> Mapping[str, RealtimePresenceInfo]:
691
+ """Return an immutable snapshot of the clients currently present."""
692
+ self._ensure_presence()
693
+ return MappingProxyType(dict(self._presence_state))
694
+
695
+ @property
696
+ def tracked_state(self) -> Mapping[str, JSONValue]:
697
+ """Return an immutable snapshot of this client's local presence state."""
698
+ self._ensure_presence()
699
+ return MappingProxyType(dict(self._tracked_state))
700
+
701
+ def _ensure_presence(self) -> None:
702
+ if self._type != "presence":
703
+ raise ValueError(PRESENCE_ONLY)
704
+
705
+ def _capture_postgres_delivery_identity(self) -> _PostgresDeliveryIdentity:
706
+ return _PostgresDeliveryIdentity(
707
+ session_lineage=self._postgres_session_lineage,
708
+ subscription_epoch=self._postgres_epoch,
709
+ )
710
+
711
+ async def _begin_postgres_epoch(self) -> None:
712
+ if self._type != "postgres":
713
+ return
714
+ await self._stop_postgres_worker()
715
+ self._postgres_epoch += 1
716
+ self._postgres_session_lineage = self._realtime._connection_lineage()
717
+
718
+ async def _end_postgres_epoch(self) -> None:
719
+ if self._type != "postgres":
720
+ return
721
+ self._postgres_epoch += 1
722
+ await self._stop_postgres_worker()
723
+
724
+ async def _stop_postgres_worker(self) -> None:
725
+ async with self._postgres_lock:
726
+ worker = self._postgres_worker
727
+ self._postgres_worker = None
728
+ if worker is not None:
729
+ await worker.abort()
730
+
731
+ def _postgres_delivery_is_current(
732
+ self,
733
+ identity: _PostgresDeliveryIdentity,
734
+ ) -> bool:
735
+ _generation, lineage, _session = (
736
+ self._realtime._client_context._capture_session_binding()
737
+ )
738
+ return (
739
+ self._subscribed
740
+ and identity.subscription_epoch == self._postgres_epoch
741
+ and identity.session_lineage == lineage
742
+ )
743
+
744
+ def _has_postgres_listener(self, change: PostgresChange) -> bool:
745
+ for callback in self._callbacks.get("*", []):
746
+ listener_filter = self._postgres_filters.get(id(callback))
747
+ if listener_filter is None:
748
+ return True
749
+ event, schema, table = listener_filter
750
+ if (
751
+ event in ("*", change.type)
752
+ and schema == change.schema
753
+ and table == change.table
754
+ ):
755
+ return True
756
+ return False
757
+
758
+ def _postgres_fetch_request(
759
+ self,
760
+ change: PostgresChange,
761
+ ) -> _PostgresFetchRequest | None:
762
+ database_name = self._realtime.database_name
763
+ if (
764
+ not self._fetch_config.enabled
765
+ or change.mode != "lightweight"
766
+ or change.type == "DELETE"
767
+ or change.schema != "public"
768
+ or change.id is None
769
+ or database_name is None
770
+ ):
771
+ return None
772
+ return _PostgresFetchRequest(
773
+ database_name=database_name,
774
+ access_token=self._realtime._connection_token(),
775
+ table=change.table,
776
+ row_id=change.id,
777
+ )
778
+
779
+ async def _receive_postgres_change(self, data: Any) -> None:
780
+ change = _postgres_change(data)
781
+ if change is None or not self._has_postgres_listener(change):
782
+ return
783
+ if change.mode == "lightweight" and change.type == "DELETE":
784
+ old_record = change.old_record
785
+ if old_record is None and change.id is not None:
786
+ old_record = {"id": change.id}
787
+ change = replace(change, old_record=old_record, id=None, mode=None)
788
+ identity = self._capture_postgres_delivery_identity()
789
+ if not self._postgres_delivery_is_current(identity):
790
+ return
791
+ delivery = _PostgresDelivery(change=change, identity=identity)
792
+ request = self._postgres_fetch_request(change)
793
+ async with self._postgres_lock:
794
+ if not self._postgres_delivery_is_current(identity):
795
+ return
796
+ worker = self._postgres_worker
797
+ if worker is None:
798
+ worker = PostgresFetchWorker(
799
+ self._realtime._fetch_postgres_rows,
800
+ self._deliver_postgres,
801
+ queue_limit=POSTGRES_QUEUE_LIMIT,
802
+ batch_window_seconds=self._fetch_config.batch_window_seconds,
803
+ max_batch_size=self._fetch_config.max_batch_size,
804
+ )
805
+ self._postgres_worker = worker
806
+ try:
807
+ await worker.enqueue(PostgresFetchJob(request=request, fallback=delivery))
808
+ except RuntimeError:
809
+ if self._postgres_delivery_is_current(identity):
810
+ raise
811
+
812
+ async def _deliver_postgres(
813
+ self,
814
+ outcome: PostgresFetchOutcome[_PostgresDelivery],
815
+ ) -> None:
816
+ delivery = outcome.job.fallback
817
+ if not self._postgres_delivery_is_current(delivery.identity):
818
+ return
819
+ change = delivery.change
820
+ if outcome.record is not None:
821
+ change = replace(change, record=outcome.record, id=None, mode=None)
822
+ elif outcome.job.request is not None:
823
+ error = outcome.error
824
+ if error is None:
825
+ identifier = (
826
+ f"{change.schema}.{change.table}:{outcome.job.request.row_id}"
827
+ )
828
+ message = f"Postgres row not found: {identifier}"
829
+ error = LookupError(message)
830
+ asyncio.get_running_loop().call_exception_handler(
831
+ {
832
+ "message": POSTGRES_FETCH_FAILED_MESSAGE,
833
+ "exception": error,
834
+ "channel": self._name,
835
+ }
836
+ )
837
+ if self._postgres_delivery_is_current(delivery.identity):
838
+ await self._emit(
839
+ "*",
840
+ change,
841
+ postgres_identity=delivery.identity,
842
+ )
843
+
844
+ async def subscribe(self) -> None:
845
+ """Wait until this channel is subscribed and ready for use."""
846
+ await self._realtime._subscribe(self)
847
+
848
+ async def send(self, data: Any) -> None:
849
+ """Publish a broadcast payload to this channel."""
850
+ await self._realtime._publish(self, data)
851
+
852
+ async def unsubscribe(self) -> None:
853
+ """Unsubscribe from this channel."""
854
+ await self._realtime._unsubscribe(self)
855
+
856
+ async def _emit(
857
+ self,
858
+ event: str,
859
+ data: Any,
860
+ *,
861
+ postgres_identity: _PostgresDeliveryIdentity | None = None,
862
+ ) -> None:
863
+ if not self._callbacks.get(event):
864
+ return
865
+ if event == "presence_sync":
866
+ self._pending_presence_sync = NO_PENDING_CALLBACK
867
+ try:
868
+ self._callback_queue.put_nowait(
869
+ _CallbackDelivery(
870
+ event,
871
+ data,
872
+ postgres_identity,
873
+ self._callback_epoch(event) if postgres_identity is None else None,
874
+ )
875
+ )
876
+ except asyncio.QueueFull:
877
+ if event == "presence_sync":
878
+ self._pending_presence_sync = data
879
+ return
880
+ asyncio.get_running_loop().call_exception_handler(
881
+ {
882
+ "message": CALLBACK_QUEUE_FULL_MESSAGE,
883
+ "channel": self._name,
884
+ }
885
+ )
886
+ task = self._callback_task
887
+ if task is None or task.done():
888
+ self._start_callback_dispatcher()
889
+
890
+ def _start_callback_dispatcher(self) -> None:
891
+ task = asyncio.create_task(self._dispatch_callbacks())
892
+ self._callback_task = task
893
+ # Retain running application work even if its channel is removed.
894
+ self._realtime._callback_tasks.add(task)
895
+ task.add_done_callback(self._callback_dispatcher_finished)
896
+
897
+ def _callback_dispatcher_finished(self, task: asyncio.Task[None]) -> None:
898
+ self._realtime._callback_tasks.discard(task)
899
+ if self._callback_task is task:
900
+ self._callback_task = None
901
+ if not task.cancelled() and (error := task.exception()) is not None:
902
+ asyncio.get_running_loop().call_exception_handler(
903
+ {
904
+ "message": "Volcano realtime callback dispatcher failed",
905
+ "exception": error,
906
+ "channel": self._name,
907
+ }
908
+ )
909
+
910
+ async def _dispatch_callbacks(self) -> None:
911
+ try:
912
+ # Register the worker before user code can re-enter through eager tasks.
913
+ await asyncio.sleep(0)
914
+ while not self._callback_queue.empty():
915
+ delivery = self._callback_queue.get_nowait()
916
+ try:
917
+ if not self._callback_delivery_is_current(delivery):
918
+ continue
919
+ for callback in tuple(self._callbacks.get(delivery.event, [])):
920
+ if not self._callback_delivery_is_current(delivery):
921
+ break
922
+ # Isolate a callback's own cancellation from later delivery.
923
+ (error,) = await asyncio.gather(
924
+ self._run_callback(callback, delivery),
925
+ return_exceptions=True,
926
+ )
927
+ if isinstance(error, BaseException):
928
+ asyncio.get_running_loop().call_exception_handler(
929
+ {
930
+ "message": "Volcano realtime callback failed",
931
+ "exception": error,
932
+ "channel": self._name,
933
+ }
934
+ )
935
+ finally:
936
+ self._callback_queue.task_done()
937
+ self._enqueue_pending_presence_sync()
938
+ finally:
939
+ # Event-loop cancellation must not leave queued delivery to restart.
940
+ self._discard_callbacks()
941
+
942
+ def _callback_delivery_is_current(self, delivery: _CallbackDelivery) -> bool:
943
+ if delivery.delivery_epoch is not None:
944
+ return (
945
+ not self._paused or delivery.event == "presence_sync"
946
+ ) and delivery.delivery_epoch == self._callback_epoch(delivery.event)
947
+ identity = delivery.postgres_identity
948
+ return identity is None or self._postgres_delivery_is_current(identity)
949
+
950
+ def _callback_epoch(self, event: str) -> int:
951
+ if event in {"join", "leave", "presence_sync"}:
952
+ return self._presence_epoch
953
+ return self._delivery_epoch
954
+
955
+ def _enqueue_pending_presence_sync(self) -> None:
956
+ pending = self._pending_presence_sync
957
+ if pending is NO_PENDING_CALLBACK or self._callback_queue.full():
958
+ return
959
+ self._pending_presence_sync = NO_PENDING_CALLBACK
960
+ self._callback_queue.put_nowait(
961
+ _CallbackDelivery(
962
+ "presence_sync", pending, delivery_epoch=self._presence_epoch
963
+ )
964
+ )
965
+
966
+ async def _run_callback(
967
+ self,
968
+ callback: MessageCallback,
969
+ delivery: _CallbackDelivery,
970
+ ) -> None:
971
+ if not self._callback_delivery_is_current(delivery):
972
+ return
973
+ result = callback(delivery.data)
974
+ if inspect.isawaitable(result):
975
+ await result
976
+
977
+ def _replace_presence(self, clients: Mapping[str, Any]) -> None:
978
+ self._presence_state = {
979
+ client_id: self._presence_info(info) for client_id, info in clients.items()
980
+ }
981
+
982
+ async def _begin_presence_sync(self) -> None:
983
+ async with self._presence_lock:
984
+ self._presence_syncing = True
985
+ self._presence_events.clear()
986
+
987
+ async def _complete_presence_sync(self, clients: Mapping[str, Any]) -> None:
988
+ async with self._presence_lock:
989
+ if not self._subscribed:
990
+ self._discard_presence_sync()
991
+ return
992
+ self._replace_presence(clients)
993
+ for event, presence in self._presence_events:
994
+ self._apply_presence_event(event, presence)
995
+ self._discard_presence_sync()
996
+ await self._emit("presence_sync", self.get_presence_state())
997
+
998
+ async def _abort_presence_sync(self) -> None:
999
+ async with self._presence_lock:
1000
+ self._discard_presence_sync()
1001
+
1002
+ async def _fail_presence_sync(self) -> None:
1003
+ async with self._presence_lock:
1004
+ self._discard_presence_sync()
1005
+ if not self._subscribed:
1006
+ return
1007
+ self._presence_state.clear()
1008
+ await self._emit("presence_sync", self.get_presence_state())
1009
+
1010
+ def _discard_presence_sync(self) -> None:
1011
+ self._presence_syncing = False
1012
+ self._presence_events.clear()
1013
+
1014
+ def _apply_presence_event(
1015
+ self,
1016
+ event: str,
1017
+ presence: RealtimePresenceInfo,
1018
+ ) -> None:
1019
+ if event == "join":
1020
+ self._presence_state[presence.client] = presence
1021
+ else:
1022
+ self._presence_state.pop(presence.client, None)
1023
+
1024
+ async def _presence_join(self, info: Any) -> None:
1025
+ if self._type != "presence" or info is None:
1026
+ return
1027
+ async with self._presence_lock:
1028
+ if not self._subscribed:
1029
+ return
1030
+ presence = self._presence_info(info)
1031
+ if self._presence_syncing:
1032
+ self._presence_events.append(("join", presence))
1033
+ self._apply_presence_event("join", presence)
1034
+ await self._emit("join", presence)
1035
+ await self._emit("presence_sync", self.get_presence_state())
1036
+
1037
+ async def _presence_leave(self, info: Any) -> None:
1038
+ if self._type != "presence" or info is None:
1039
+ return
1040
+ async with self._presence_lock:
1041
+ if not self._subscribed:
1042
+ return
1043
+ presence = self._presence_info(info)
1044
+ if self._presence_syncing:
1045
+ self._presence_events.append(("leave", presence))
1046
+ self._apply_presence_event("leave", presence)
1047
+ await self._emit("leave", presence)
1048
+ await self._emit("presence_sync", self.get_presence_state())
1049
+
1050
+ async def _presence_unsubscribed(self) -> None:
1051
+ if self._type != "presence":
1052
+ return
1053
+ await self._cancel_presence_sync()
1054
+ async with self._presence_lock:
1055
+ self._discard_presence_sync()
1056
+ self._presence_state.clear()
1057
+ self._tracked_state = MappingProxyType({})
1058
+ await self._emit("presence_sync", self.get_presence_state())
1059
+
1060
+ def _presence_info(self, info: Any) -> RealtimePresenceInfo:
1061
+ data = getattr(info, "conn_info", None)
1062
+ typed_data = (
1063
+ cast("Mapping[str, JSONValue]", data)
1064
+ if isinstance(data, Mapping)
1065
+ else _empty_presence_data()
1066
+ )
1067
+ return RealtimePresenceInfo(
1068
+ client=str(getattr(info, "client", "")),
1069
+ user=getattr(info, "user", None),
1070
+ data=typed_data,
1071
+ )
1072
+
1073
+ def _schedule_presence_sync(self) -> None:
1074
+ task = self._presence_sync_task
1075
+ if task is not None and not task.done():
1076
+ self._presence_sync_pending = True
1077
+ return
1078
+ self._presence_sync_pending = False
1079
+ self._presence_sync_task = asyncio.create_task(self._run_presence_sync())
1080
+
1081
+ async def _run_presence_sync(self) -> None:
1082
+ try:
1083
+ while self._subscribed:
1084
+ await self._realtime._sync_presence(self)
1085
+ if not self._presence_sync_pending:
1086
+ return
1087
+ self._presence_sync_pending = False
1088
+ finally:
1089
+ if asyncio.current_task() is self._presence_sync_task:
1090
+ self._presence_sync_task = None
1091
+
1092
+ async def _wait_presence_sync(self) -> None:
1093
+ task = self._presence_sync_task
1094
+ if task is not None:
1095
+ await asyncio.shield(task)
1096
+
1097
+ async def _cancel_presence_sync(self) -> None:
1098
+ task = self._presence_sync_task
1099
+ self._presence_sync_task = None
1100
+ self._presence_sync_pending = False
1101
+ if task is None or task.done():
1102
+ return
1103
+ task.cancel()
1104
+ await asyncio.gather(task, return_exceptions=True)
1105
+
1106
+ async def _reset(self) -> None:
1107
+ self._invalidate()
1108
+ await self._end_postgres_epoch()
1109
+ await self._cancel_presence_sync()
1110
+ self._presence_state.clear()
1111
+ self._discard_presence_sync()
1112
+ self._tracked_state = MappingProxyType({})
1113
+ self._subscribed = False
1114
+
1115
+ def _invalidate(self) -> None:
1116
+ if self._readiness_task is not None:
1117
+ self._readiness_task.cancel()
1118
+ self._subscription = None
1119
+ self._subscription_events = None
1120
+ self._pause_delivery()
1121
+
1122
+ def _pause_delivery(self) -> None:
1123
+ self._paused = True
1124
+ self._subscribed = False
1125
+ self._discard_callbacks()
1126
+
1127
+ def _discard_callbacks(self, *, presence_only: bool = False) -> None:
1128
+ self._presence_epoch += 1
1129
+ if not presence_only:
1130
+ self._delivery_epoch += 1
1131
+ # Free capacity before recovered publications arrive behind a slow callback.
1132
+ for _ in range(self._callback_queue.qsize()):
1133
+ delivery = self._callback_queue.get_nowait()
1134
+ if presence_only and delivery.event == "message":
1135
+ # Requeue before task_done so queue.join cannot finish prematurely.
1136
+ self._callback_queue.put_nowait(delivery)
1137
+ self._callback_queue.task_done()
1138
+ self._pending_presence_sync = NO_PENDING_CALLBACK
1139
+
1140
+ async def _transport_lost(self) -> None:
1141
+ self._subscribed = False
1142
+ # Recoverable channels already include queued messages in their offsets.
1143
+ if not self._paused and self._type != "broadcast":
1144
+ self._discard_callbacks(presence_only=self._type == "presence")
1145
+ await self._end_postgres_epoch()
1146
+ await self._presence_unsubscribed()
1147
+
1148
+
1149
+ class Realtime:
1150
+ """Manage project realtime connections and channels."""
1151
+
1152
+ def __init__(
1153
+ self,
1154
+ client: RealtimeContext,
1155
+ *,
1156
+ api_url: str,
1157
+ client_factory: CentrifugeFactory = _centrifuge_client,
1158
+ ) -> None:
1159
+ """Create a lazily connected realtime facade."""
1160
+ self._client_context = client
1161
+ self._api_url = api_url
1162
+ self._client_factory = client_factory
1163
+ self._connection: _VolcanoCentrifugeConnection | None = None
1164
+ self._connection_session_lineage: int | None = None
1165
+ self._connection_access_token: str | None = None
1166
+ self._connection_lock = asyncio.Lock()
1167
+ self._channels: dict[str, Channel] = {}
1168
+ self._callback_tasks: set[asyncio.Task[None]] = set()
1169
+ self._removing_channels: set[str] = set()
1170
+ self._connection_callbacks: dict[str, dict[int, RealtimeCallback]] = {
1171
+ "connect": {},
1172
+ "disconnect": {},
1173
+ "error": {},
1174
+ }
1175
+ self._next_callback_id = 0
1176
+ self._connection_callback_queue: asyncio.Queue[
1177
+ tuple[str, Any, tuple[int, ...]]
1178
+ ] = asyncio.Queue(maxsize=CALLBACK_QUEUE_LIMIT)
1179
+ self._connection_callback_task: asyncio.Task[None] | None = None
1180
+ self._database_name: str | None = None
1181
+
1182
+ @property
1183
+ def database_name(self) -> str | None:
1184
+ """Return the database bound to lightweight Postgres changes."""
1185
+ return self._database_name
1186
+
1187
+ def set_database_name(self, name: str | None) -> None:
1188
+ """Bind lightweight Postgres changes to a project database."""
1189
+ self._database_name = name
1190
+
1191
+ async def _fetch_postgres_rows(
1192
+ self,
1193
+ requests: tuple[_PostgresFetchRequest, ...],
1194
+ ) -> tuple[dict[str, Any] | None, ...]:
1195
+ first = requests[0]
1196
+ row_ids = [request.row_id for request in requests]
1197
+ transport = cast(
1198
+ "AsyncDatabaseSelectTransport",
1199
+ self._client_context._transport,
1200
+ )
1201
+ response = await invoke_async(
1202
+ transport.query_database_select_async,
1203
+ authorization=first.access_token,
1204
+ database_name=first.database_name,
1205
+ body={
1206
+ "table": first.table,
1207
+ "filters": [{"column": "id", "operator": "in", "value": row_ids}],
1208
+ "limit": len(row_ids),
1209
+ },
1210
+ )
1211
+ payload = response_payload(response, 200)
1212
+ rows = list(payload["data"])
1213
+ return tuple(
1214
+ next(
1215
+ (row for row in rows if row.get("id") == request.row_id),
1216
+ None,
1217
+ )
1218
+ for request in requests
1219
+ )
1220
+
1221
+ def on_connect(self, callback: RealtimeCallback) -> UnsubscribeCallback:
1222
+ """Register a connection callback and return its unsubscribe function."""
1223
+ return self._register_connection_callback("connect", callback)
1224
+
1225
+ def on_disconnect(self, callback: RealtimeCallback) -> UnsubscribeCallback:
1226
+ """Register a disconnection callback and return its unsubscribe function."""
1227
+ return self._register_connection_callback("disconnect", callback)
1228
+
1229
+ def on_error(self, callback: RealtimeCallback) -> UnsubscribeCallback:
1230
+ """Register a transport-error callback and return its unsubscribe function."""
1231
+ return self._register_connection_callback("error", callback)
1232
+
1233
+ def _register_connection_callback(
1234
+ self,
1235
+ event: str,
1236
+ callback: RealtimeCallback,
1237
+ ) -> UnsubscribeCallback:
1238
+ if not callable(callback):
1239
+ raise TypeError(CALLBACK_NOT_CALLABLE)
1240
+ self._next_callback_id += 1
1241
+ callback_id = self._next_callback_id
1242
+ self._connection_callbacks[event][callback_id] = callback
1243
+
1244
+ def unsubscribe() -> None:
1245
+ self._connection_callbacks[event].pop(callback_id, None)
1246
+
1247
+ return unsubscribe
1248
+
1249
+ def _enqueue_connection_callbacks(self, event: str, context: Any) -> None:
1250
+ callback_ids = tuple(self._connection_callbacks[event])
1251
+ if not callback_ids:
1252
+ return
1253
+ try:
1254
+ self._connection_callback_queue.put_nowait((event, context, callback_ids))
1255
+ except asyncio.QueueFull:
1256
+ asyncio.get_running_loop().call_exception_handler(
1257
+ {"message": "Volcano realtime connection callback queue is full"}
1258
+ )
1259
+ return
1260
+ task = self._connection_callback_task
1261
+ if task is None or task.done():
1262
+ self._connection_callback_task = asyncio.create_task(
1263
+ self._drain_connection_callbacks()
1264
+ )
1265
+
1266
+ async def _drain_connection_callbacks(self) -> None:
1267
+ while not self._connection_callback_queue.empty():
1268
+ event, context, callback_ids = self._connection_callback_queue.get_nowait()
1269
+ try:
1270
+ callbacks = self._connection_callbacks[event]
1271
+ for callback_id in callback_ids:
1272
+ callback = callbacks.get(callback_id)
1273
+ if callback is None:
1274
+ continue
1275
+ (error,) = await asyncio.gather(
1276
+ self._run_connection_callback(callback, context),
1277
+ return_exceptions=True,
1278
+ )
1279
+ if isinstance(error, BaseException):
1280
+ asyncio.get_running_loop().call_exception_handler(
1281
+ {
1282
+ "message": (
1283
+ "Volcano realtime connection callback failed"
1284
+ ),
1285
+ "exception": error,
1286
+ "event": event,
1287
+ }
1288
+ )
1289
+ finally:
1290
+ self._connection_callback_queue.task_done()
1291
+
1292
+ async def _run_connection_callback(
1293
+ self,
1294
+ callback: RealtimeCallback,
1295
+ context: Any,
1296
+ ) -> None:
1297
+ result = callback(context)
1298
+ if inspect.isawaitable(result):
1299
+ await result
1300
+
1301
+ def channel(
1302
+ self,
1303
+ name: str,
1304
+ *,
1305
+ channel_type: ChannelType = "broadcast",
1306
+ auto_fetch: bool = True,
1307
+ fetch_batch_window_ms: int = POSTGRES_BATCH_WINDOW_MS,
1308
+ fetch_max_batch_size: int = POSTGRES_MAX_BATCH_SIZE,
1309
+ ) -> Channel:
1310
+ """Return a stable channel facade for a realtime name and configuration."""
1311
+ channel_type = _validate_channel_type(channel_type)
1312
+ fetch_config = _postgres_fetch_config(
1313
+ auto_fetch=auto_fetch,
1314
+ fetch_batch_window_ms=fetch_batch_window_ms,
1315
+ fetch_max_batch_size=fetch_max_batch_size,
1316
+ )
1317
+ wire_name = f"{channel_type}:{name}"
1318
+ if wire_name in self._removing_channels:
1319
+ raise RuntimeError(CHANNEL_REMOVAL_IN_PROGRESS)
1320
+ channel = self._channels.get(wire_name)
1321
+ if channel is None:
1322
+ channel = Channel(
1323
+ self,
1324
+ wire_name,
1325
+ channel_type,
1326
+ fetch_config=fetch_config,
1327
+ )
1328
+ self._channels[wire_name] = channel
1329
+ elif channel._fetch_config != fetch_config:
1330
+ message = (
1331
+ f"channel {wire_name!r} already uses a different fetch configuration"
1332
+ )
1333
+ raise ValueError(message)
1334
+ return channel
1335
+
1336
+ @property
1337
+ def is_connected(self) -> bool:
1338
+ """Return whether the realtime transport is connected."""
1339
+ return self._connection is not None and self._connection.is_connected
1340
+
1341
+ async def remove_channel(
1342
+ self,
1343
+ name: str,
1344
+ *,
1345
+ channel_type: ChannelType = "broadcast",
1346
+ ) -> None:
1347
+ """Unsubscribe and forget one broadcast or presence channel."""
1348
+ channel_type = _validate_channel_type(channel_type)
1349
+ wire_name = f"{channel_type}:{name}"
1350
+ async with self._connection_lock:
1351
+ channel = self._channels.get(wire_name)
1352
+ if channel is None:
1353
+ return
1354
+ self._removing_channels.add(wire_name)
1355
+ try:
1356
+ await self._remove_channel(channel)
1357
+ self._channels.pop(wire_name, None)
1358
+ finally:
1359
+ self._removing_channels.remove(wire_name)
1360
+
1361
+ async def remove_all_channels(self) -> None:
1362
+ """Unsubscribe and forget every managed channel."""
1363
+ async with self._connection_lock:
1364
+ first_error: Exception | None = None
1365
+ for wire_name, channel in tuple(self._channels.items()):
1366
+ self._removing_channels.add(wire_name)
1367
+ try:
1368
+ await self._remove_channel(channel)
1369
+ except CENTRIFUGE_ERROR as error:
1370
+ first_error = first_error or error
1371
+ else:
1372
+ if self._channels.get(wire_name) is channel:
1373
+ del self._channels[wire_name]
1374
+ finally:
1375
+ self._removing_channels.remove(wire_name)
1376
+ if first_error is not None:
1377
+ raise first_error
1378
+
1379
+ async def _remove_channel(self, channel: Channel) -> None:
1380
+ channel._subscribe_generation += 1
1381
+ await self._discard_subscription(channel)
1382
+ await channel._reset()
1383
+
1384
+ async def _discard_subscription(self, channel: Channel) -> None:
1385
+ subscription = channel._subscription
1386
+ channel._subscription_events = None
1387
+ channel._pause_delivery()
1388
+ try:
1389
+ if subscription is not None:
1390
+ # Native state must change before any cancellable local cleanup.
1391
+ await _unsubscribe_native(subscription)
1392
+ finally:
1393
+ await channel._transport_lost()
1394
+ if subscription is not None and self._connection is not None:
1395
+ self._connection.remove_subscription(subscription)
1396
+ channel._subscription = None
1397
+
1398
+ async def _token(self) -> str:
1399
+ lineage = self._connection_lineage()
1400
+ session = self._session_for_lineage(lineage)
1401
+ self._connection_access_token = session.access_token
1402
+ return session.access_token
1403
+
1404
+ def _session_for_lineage(self, expected_lineage: int) -> Session:
1405
+ _generation, lineage, session = self._client_context._capture_session_binding()
1406
+ if session is None:
1407
+ raise RuntimeError(NO_ACTIVE_SESSION)
1408
+ if lineage != expected_lineage:
1409
+ raise RuntimeError(CONNECTION_SESSION_CHANGED)
1410
+ return session
1411
+
1412
+ def _connection_lineage(self) -> int:
1413
+ lineage = self._connection_session_lineage
1414
+ if lineage is None:
1415
+ raise RuntimeError(CONNECTION_SESSION_UNAVAILABLE)
1416
+ return lineage
1417
+
1418
+ def _connection_token(self) -> str:
1419
+ token = self._connection_access_token
1420
+ if token is None:
1421
+ raise RuntimeError(CONNECTION_SESSION_UNAVAILABLE)
1422
+ return token
1423
+
1424
+ def _address(self) -> str:
1425
+ parsed = urlsplit(self._api_url)
1426
+ scheme = "wss" if parsed.scheme == "https" else "ws"
1427
+ query = f"apikey={quote(self._client_context._anon_token(), safe='')}"
1428
+ return urlunsplit((scheme, parsed.netloc, "/realtime/v1/websocket", query, ""))
1429
+
1430
+ async def _connect(self) -> _VolcanoCentrifugeConnection:
1431
+ async with self._connection_lock:
1432
+ return await self._connect_locked()
1433
+
1434
+ async def _connect_locked(self) -> _VolcanoCentrifugeConnection:
1435
+ if self._connection is not None:
1436
+ self._session_for_lineage(self._connection_lineage())
1437
+ return self._connection
1438
+ _generation, lineage, session = self._client_context._capture_session_binding()
1439
+ if session is None:
1440
+ raise RuntimeError(NO_ACTIVE_SESSION)
1441
+ connection = _VolcanoCentrifugeConnection(
1442
+ self._client_factory(
1443
+ self._address(),
1444
+ events=_ClientEvents(self),
1445
+ token=session.access_token,
1446
+ get_token=self._token,
1447
+ )
1448
+ )
1449
+ self._connection_session_lineage = lineage
1450
+ self._connection_access_token = session.access_token
1451
+ try:
1452
+ await connection.connect()
1453
+ except BaseException:
1454
+ self._connection_session_lineage = None
1455
+ self._connection_access_token = None
1456
+ raise
1457
+ try:
1458
+ current_session = self._session_for_lineage(lineage)
1459
+ except RuntimeError:
1460
+ self._connection = connection
1461
+ await connection.disconnect()
1462
+ self._connection = None
1463
+ self._connection_session_lineage = None
1464
+ self._connection_access_token = None
1465
+ raise
1466
+ self._connection_access_token = current_session.access_token
1467
+ self._connection = connection
1468
+ return connection
1469
+
1470
+ async def _subscribe(self, channel: Channel) -> None:
1471
+ # A later stop supersedes this request, including time spent waiting for locks.
1472
+ generation = channel._subscribe_generation
1473
+ async with channel._subscribe_lock:
1474
+ subscription = None
1475
+ try:
1476
+ async with self._connection_lock:
1477
+ subscription = await self._prepare_subscription(channel, generation)
1478
+ if channel._subscribed:
1479
+ return
1480
+ channel._paused = False
1481
+ await subscription.subscribe()
1482
+ channel._readiness_task = asyncio.create_task(
1483
+ self._wait_subscription(channel, subscription)
1484
+ )
1485
+ try:
1486
+ await channel._readiness_task
1487
+ finally:
1488
+ channel._readiness_task = None
1489
+ except BaseException as error:
1490
+ if (
1491
+ subscription is None
1492
+ or channel._subscription is not subscription
1493
+ or channel._paused
1494
+ ):
1495
+ # An explicit pause or removal owns the newer subscription intent.
1496
+ raise
1497
+ channel._subscribe_generation += 1
1498
+ channel._subscription_events = None
1499
+ channel._pause_delivery()
1500
+ try:
1501
+ async with self._connection_lock:
1502
+ if channel._subscription is subscription:
1503
+ await self._discard_subscription(channel)
1504
+ except CENTRIFUGE_ERROR:
1505
+ error.add_note("Failed to clean up the realtime subscription")
1506
+ raise
1507
+
1508
+ async def _prepare_subscription(
1509
+ self, channel: Channel, generation: int
1510
+ ) -> CentrifugeSubscription:
1511
+ if generation != channel._subscribe_generation:
1512
+ raise asyncio.CancelledError
1513
+ if self._channels.get(channel._name) is not channel:
1514
+ raise RuntimeError(CHANNEL_NOT_MANAGED)
1515
+ connection = await self._connect_locked()
1516
+ if channel._subscription is not None and channel._subscription_events is None:
1517
+ await self._discard_subscription(channel)
1518
+ if channel._subscription is None:
1519
+ channel._subscription_events = _ChannelEvents(channel)
1520
+ channel._subscription = connection.new_subscription(
1521
+ channel._name,
1522
+ events=channel._subscription_events,
1523
+ join_leave=channel._type == "presence",
1524
+ recoverable=channel._type != "postgres",
1525
+ )
1526
+ return channel._subscription
1527
+
1528
+ async def _wait_subscription(
1529
+ self, channel: Channel, subscription: CentrifugeSubscription
1530
+ ) -> None:
1531
+ await subscription.ready()
1532
+ if channel._type == "presence":
1533
+ await channel._wait_presence_sync()
1534
+ if channel._subscription is not subscription or not channel._subscribed:
1535
+ message = "realtime subscription was interrupted"
1536
+ raise RuntimeError(message)
1537
+
1538
+ async def _sync_presence(self, channel: Channel) -> None:
1539
+ if channel._subscription is None:
1540
+ return
1541
+ await channel._begin_presence_sync()
1542
+ query_succeeded = False
1543
+ try:
1544
+ # Native replies must settle even after the roster refresh is cancelled.
1545
+ query = asyncio.create_task(channel._subscription.presence())
1546
+ query.add_done_callback(_consume_presence_result)
1547
+ result = await asyncio.shield(query)
1548
+ query_succeeded = True
1549
+ except CENTRIFUGE_ERROR as error:
1550
+ await channel._fail_presence_sync()
1551
+ query_succeeded = True
1552
+ self._enqueue_connection_callbacks(
1553
+ "error",
1554
+ RealtimeErrorContext(
1555
+ code=getattr(error, "code", None),
1556
+ message=str(error),
1557
+ error=error,
1558
+ ),
1559
+ )
1560
+ return
1561
+ finally:
1562
+ if not query_succeeded:
1563
+ await channel._abort_presence_sync()
1564
+ clients = getattr(result, "clients", None)
1565
+ if isinstance(clients, Mapping):
1566
+ await channel._complete_presence_sync(cast("Mapping[str, Any]", clients))
1567
+ else:
1568
+ await channel._abort_presence_sync()
1569
+
1570
+ async def _publish(self, channel: Channel, data: Any) -> None:
1571
+ async with self._connection_lock:
1572
+ if channel._type != "broadcast":
1573
+ raise ValueError(BROADCAST_ONLY)
1574
+ subscription = channel._subscription
1575
+ if not channel._subscribed or subscription is None:
1576
+ raise RuntimeError(CHANNEL_NOT_SUBSCRIBED)
1577
+ await subscription.publish(data)
1578
+
1579
+ async def _unsubscribe(self, channel: Channel) -> None:
1580
+ async with self._connection_lock:
1581
+ channel._subscribe_generation += 1
1582
+ if not channel._paused:
1583
+ channel._pause_delivery()
1584
+ if channel._subscription is not None:
1585
+ await _unsubscribe_native(channel._subscription)
1586
+
1587
+ async def disconnect(self) -> None:
1588
+ """Disconnect and reset every channel managed by this facade."""
1589
+ async with self._connection_lock:
1590
+ connection = self._connection
1591
+ self._connection = None
1592
+ channels = tuple(self._channels.values())
1593
+ for channel in channels:
1594
+ channel._subscribe_generation += 1
1595
+ channel._invalidate()
1596
+ cancelled: asyncio.CancelledError | None = None
1597
+ try:
1598
+ for channel in channels:
1599
+ try:
1600
+ await channel._reset()
1601
+ except asyncio.CancelledError as error:
1602
+ cancelled = error
1603
+ finally:
1604
+ try:
1605
+ if connection is not None:
1606
+ await connection.disconnect()
1607
+ finally:
1608
+ self._connection_session_lineage = None
1609
+ self._connection_access_token = None
1610
+ if cancelled is not None:
1611
+ raise cancelled