integrations.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. """W&B Public API for integrations.
  2. This module provides classes for interacting with W&B integrations.
  3. """
  4. from __future__ import annotations
  5. from typing import TYPE_CHECKING, Any, ClassVar, Union
  6. from typing_extensions import override
  7. from wandb_gql import gql
  8. from wandb.apis.paginator import RelayPaginator
  9. if TYPE_CHECKING:
  10. from wandb_graphql.language.ast import Document
  11. from wandb._pydantic import Connection
  12. from wandb.apis.public.api import RetryingClient
  13. from wandb.automations import Integration, SlackIntegration, WebhookIntegration
  14. from wandb.automations._generated import (
  15. SlackIntegrationFields,
  16. WebhookIntegrationFields,
  17. )
  18. IntegrationFields = Union[SlackIntegrationFields, WebhookIntegrationFields]
  19. class Integrations(RelayPaginator["IntegrationFields", "Integration"]):
  20. """A lazy iterator of `Integration` objects.
  21. <!-- lazydoc-ignore-class: internal -->
  22. """
  23. QUERY: ClassVar[Document | None] = None
  24. last_response: Connection[IntegrationFields] | None
  25. def __init__(
  26. self,
  27. client: RetryingClient,
  28. variables: dict[str, Any],
  29. per_page: int = 50,
  30. ):
  31. if self.QUERY is None:
  32. from wandb.automations._generated import INTEGRATIONS_BY_ENTITY_GQL
  33. type(self).QUERY = gql(INTEGRATIONS_BY_ENTITY_GQL)
  34. super().__init__(client, variables=variables, per_page=per_page)
  35. @override
  36. def _update_response(self) -> None:
  37. """Fetch and parse the response data for the current page."""
  38. from wandb._pydantic import Connection
  39. from wandb.automations._generated import IntegrationsByEntity
  40. data = self.client.execute(self.QUERY, variable_values=self.variables)
  41. result = IntegrationsByEntity.model_validate(data)
  42. if not ((entity := result.entity) and (conn := entity.integrations)):
  43. raise ValueError("Unexpected response data")
  44. self.last_response = Connection.model_validate(conn)
  45. def _convert(self, node: IntegrationFields) -> Integration:
  46. from wandb.automations.integrations import IntegrationAdapter
  47. return IntegrationAdapter.validate_python(node)
  48. # The paginators below filter on `typename__` since the GQL response still
  49. # includes all `Integration` types. Applying a `@skip/@include` directive
  50. # does not change this. Restricting results to a single type requires
  51. # a client-side filter.
  52. class WebhookIntegrations(Integrations):
  53. """A lazy iterator of `WebhookIntegration` objects.
  54. <!-- lazydoc-ignore-class: internal -->
  55. """
  56. def _convert(self, node: IntegrationFields) -> WebhookIntegration:
  57. return node if (node.typename__ == "GenericWebhookIntegration") else None
  58. class SlackIntegrations(Integrations):
  59. """A lazy iterator of `SlackIntegration` objects.
  60. <!-- lazydoc-ignore-class: internal -->
  61. """
  62. def _convert(self, node: IntegrationFields) -> SlackIntegration:
  63. return node if (node.typename__ == "SlackIntegration") else None