airbyte-agent-slack 0.1.20__py3-none-any.whl → 0.1.21__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.
@@ -18,8 +18,8 @@ from .models import (
18
18
  ChannelsListResponse,
19
19
  ChannelResponse,
20
20
  Reaction,
21
- Attachment,
22
21
  File,
22
+ Attachment,
23
23
  Message,
24
24
  Thread,
25
25
  EditedInfo,
@@ -50,7 +50,13 @@ from .models import (
50
50
  UsersListResult,
51
51
  ChannelsListResult,
52
52
  ChannelMessagesListResult,
53
- ThreadsListResult
53
+ ThreadsListResult,
54
+ AirbyteSearchHit,
55
+ AirbyteSearchResult,
56
+ ChannelsSearchData,
57
+ ChannelsSearchResult,
58
+ UsersSearchData,
59
+ UsersSearchResult
54
60
  )
55
61
  from .types import (
56
62
  UsersListParams,
@@ -65,7 +71,15 @@ from .types import (
65
71
  ChannelsUpdateParams,
66
72
  ChannelTopicsCreateParams,
67
73
  ChannelPurposesCreateParams,
68
- ReactionsCreateParams
74
+ ReactionsCreateParams,
75
+ AirbyteSearchParams,
76
+ AirbyteSortOrder,
77
+ ChannelsSearchFilter,
78
+ ChannelsSearchQuery,
79
+ ChannelsCondition,
80
+ UsersSearchFilter,
81
+ UsersSearchQuery,
82
+ UsersCondition
69
83
  )
70
84
 
71
85
  __all__ = [
@@ -82,8 +96,8 @@ __all__ = [
82
96
  "ChannelsListResponse",
83
97
  "ChannelResponse",
84
98
  "Reaction",
85
- "Attachment",
86
99
  "File",
100
+ "Attachment",
87
101
  "Message",
88
102
  "Thread",
89
103
  "EditedInfo",
@@ -115,6 +129,12 @@ __all__ = [
115
129
  "ChannelsListResult",
116
130
  "ChannelMessagesListResult",
117
131
  "ThreadsListResult",
132
+ "AirbyteSearchHit",
133
+ "AirbyteSearchResult",
134
+ "ChannelsSearchData",
135
+ "ChannelsSearchResult",
136
+ "UsersSearchData",
137
+ "UsersSearchResult",
118
138
  "UsersListParams",
119
139
  "UsersGetParams",
120
140
  "ChannelsListParams",
@@ -128,4 +148,12 @@ __all__ = [
128
148
  "ChannelTopicsCreateParams",
129
149
  "ChannelPurposesCreateParams",
130
150
  "ReactionsCreateParams",
151
+ "AirbyteSearchParams",
152
+ "AirbyteSortOrder",
153
+ "ChannelsSearchFilter",
154
+ "ChannelsSearchQuery",
155
+ "ChannelsCondition",
156
+ "UsersSearchFilter",
157
+ "UsersSearchQuery",
158
+ "UsersCondition",
131
159
  ]
@@ -1032,7 +1032,9 @@ class LocalExecutor:
1032
1032
  if "variables" in graphql_config and graphql_config["variables"]:
1033
1033
  variables = self._interpolate_variables(graphql_config["variables"], params, param_defaults)
1034
1034
  # Filter out None values (optional fields not provided) - matches REST _extract_body() behavior
1035
- body["variables"] = {k: v for k, v in variables.items() if v is not None}
1035
+ # But preserve None for variables explicitly marked as nullable (e.g., to unassign a user)
1036
+ nullable_vars = set(graphql_config.get("x-airbyte-nullable-variables") or [])
1037
+ body["variables"] = {k: v for k, v in variables.items() if v is not None or k in nullable_vars}
1036
1038
 
1037
1039
  # Add operation name if specified
1038
1040
  if "operationName" in graphql_config:
@@ -134,6 +134,11 @@ class GraphQLBodyConfig(BaseModel):
134
134
  None,
135
135
  description="Default fields to select if not provided in request parameters. Can be a string or array of field names.",
136
136
  )
137
+ nullable_variables: List[str] | None = Field(
138
+ default=None,
139
+ alias="x-airbyte-nullable-variables",
140
+ description="Variable names that can be explicitly set to null (e.g., to unassign a user)",
141
+ )
137
142
 
138
143
 
139
144
  # Union type for all body type configs (extensible for future types like XML, SOAP, etc.)
@@ -27,6 +27,11 @@ from .types import (
27
27
  ThreadsListParams,
28
28
  UsersGetParams,
29
29
  UsersListParams,
30
+ AirbyteSearchParams,
31
+ ChannelsSearchFilter,
32
+ ChannelsSearchQuery,
33
+ UsersSearchFilter,
34
+ UsersSearchQuery,
30
35
  )
31
36
  if TYPE_CHECKING:
32
37
  from .models import SlackAuthConfig
@@ -46,6 +51,12 @@ from .models import (
46
51
  ReactionAddResponse,
47
52
  Thread,
48
53
  User,
54
+ AirbyteSearchHit,
55
+ AirbyteSearchResult,
56
+ ChannelsSearchData,
57
+ ChannelsSearchResult,
58
+ UsersSearchData,
59
+ UsersSearchResult,
49
60
  )
50
61
 
51
62
  # TypeVar for decorator type preservation
@@ -61,7 +72,7 @@ class SlackConnector:
61
72
  """
62
73
 
63
74
  connector_name = "slack"
64
- connector_version = "0.1.7"
75
+ connector_version = "0.1.8"
65
76
  vendored_sdk_version = "0.1.0" # Version of vendored connector-sdk
66
77
 
67
78
  # Map of (entity, action) -> needs_envelope for envelope wrapping decision
@@ -522,6 +533,82 @@ class UsersQuery:
522
533
 
523
534
 
524
535
 
536
+ async def search(
537
+ self,
538
+ query: UsersSearchQuery,
539
+ limit: int | None = None,
540
+ cursor: str | None = None,
541
+ fields: list[list[str]] | None = None,
542
+ ) -> UsersSearchResult:
543
+ """
544
+ Search users records from Airbyte cache.
545
+
546
+ This operation searches cached data from Airbyte syncs.
547
+ Only available in hosted execution mode.
548
+
549
+ Available filter fields (UsersSearchFilter):
550
+ - color: The color assigned to the user for visual purposes.
551
+ - deleted: Indicates if the user is deleted or not.
552
+ - has_2fa: Flag indicating if the user has two-factor authentication enabled.
553
+ - id: Unique identifier for the user.
554
+ - is_admin: Flag specifying if the user is an admin or not.
555
+ - is_app_user: Specifies if the user is an app user.
556
+ - is_bot: Indicates if the user is a bot account.
557
+ - is_email_confirmed: Flag indicating if the user's email is confirmed.
558
+ - is_forgotten: Specifies if the user is marked as forgotten.
559
+ - is_invited_user: Indicates if the user is invited or not.
560
+ - is_owner: Flag indicating if the user is an owner.
561
+ - is_primary_owner: Specifies if the user is the primary owner.
562
+ - is_restricted: Flag specifying if the user is restricted.
563
+ - is_ultra_restricted: Indicates if the user has ultra-restricted access.
564
+ - name: The username of the user.
565
+ - profile: User's profile information containing detailed details.
566
+ - real_name: The real name of the user.
567
+ - team_id: Unique identifier for the team the user belongs to.
568
+ - tz: Timezone of the user.
569
+ - tz_label: Label representing the timezone of the user.
570
+ - tz_offset: Offset of the user's timezone.
571
+ - updated: Timestamp of when the user's information was last updated.
572
+ - who_can_share_contact_card: Specifies who can share the user's contact card.
573
+
574
+ Args:
575
+ query: Filter and sort conditions. Supports operators like eq, neq, gt, gte, lt, lte,
576
+ in, like, fuzzy, keyword, not, and, or. Example: {"filter": {"eq": {"status": "active"}}}
577
+ limit: Maximum results to return (default 1000)
578
+ cursor: Pagination cursor from previous response's next_cursor
579
+ fields: Field paths to include in results. Each path is a list of keys for nested access.
580
+ Example: [["id"], ["user", "name"]] returns id and user.name fields.
581
+
582
+ Returns:
583
+ UsersSearchResult with hits (list of AirbyteSearchHit[UsersSearchData]) and pagination info
584
+
585
+ Raises:
586
+ NotImplementedError: If called in local execution mode
587
+ """
588
+ params: dict[str, Any] = {"query": query}
589
+ if limit is not None:
590
+ params["limit"] = limit
591
+ if cursor is not None:
592
+ params["cursor"] = cursor
593
+ if fields is not None:
594
+ params["fields"] = fields
595
+
596
+ result = await self._connector.execute("users", "search", params)
597
+
598
+ # Parse response into typed result
599
+ return UsersSearchResult(
600
+ hits=[
601
+ AirbyteSearchHit[UsersSearchData](
602
+ id=hit.get("id"),
603
+ score=hit.get("score"),
604
+ data=UsersSearchData(**hit.get("data", {}))
605
+ )
606
+ for hit in result.get("hits", [])
607
+ ],
608
+ next_cursor=result.get("next_cursor"),
609
+ took_ms=result.get("took_ms")
610
+ )
611
+
525
612
  class ChannelsQuery:
526
613
  """
527
614
  Query class for Channels entity operations.
@@ -650,6 +737,90 @@ class ChannelsQuery:
650
737
 
651
738
 
652
739
 
740
+ async def search(
741
+ self,
742
+ query: ChannelsSearchQuery,
743
+ limit: int | None = None,
744
+ cursor: str | None = None,
745
+ fields: list[list[str]] | None = None,
746
+ ) -> ChannelsSearchResult:
747
+ """
748
+ Search channels records from Airbyte cache.
749
+
750
+ This operation searches cached data from Airbyte syncs.
751
+ Only available in hosted execution mode.
752
+
753
+ Available filter fields (ChannelsSearchFilter):
754
+ - context_team_id: The unique identifier of the team context in which the channel exists.
755
+ - created: The timestamp when the channel was created.
756
+ - creator: The ID of the user who created the channel.
757
+ - id: The unique identifier of the channel.
758
+ - is_archived: Indicates if the channel is archived.
759
+ - is_channel: Indicates if the entity is a channel.
760
+ - is_ext_shared: Indicates if the channel is externally shared.
761
+ - is_general: Indicates if the channel is a general channel in the workspace.
762
+ - is_group: Indicates if the channel is a group (private channel) rather than a regular channel.
763
+ - is_im: Indicates if the entity is a direct message (IM) channel.
764
+ - is_member: Indicates if the calling user is a member of the channel.
765
+ - is_mpim: Indicates if the entity is a multiple person direct message (MPIM) channel.
766
+ - is_org_shared: Indicates if the channel is organization-wide shared.
767
+ - is_pending_ext_shared: Indicates if the channel is pending external shared.
768
+ - is_private: Indicates if the channel is a private channel.
769
+ - is_read_only: Indicates if the channel is read-only.
770
+ - is_shared: Indicates if the channel is shared.
771
+ - last_read: The timestamp of the user's last read message in the channel.
772
+ - locale: The locale of the channel.
773
+ - name: The name of the channel.
774
+ - name_normalized: The normalized name of the channel.
775
+ - num_members: The number of members in the channel.
776
+ - parent_conversation: The parent conversation of the channel.
777
+ - pending_connected_team_ids: The IDs of teams that are pending to be connected to the channel.
778
+ - pending_shared: The list of pending shared items of the channel.
779
+ - previous_names: The previous names of the channel.
780
+ - purpose: The purpose of the channel.
781
+ - shared_team_ids: The IDs of teams with which the channel is shared.
782
+ - topic: The topic of the channel.
783
+ - unlinked: Indicates if the channel is unlinked.
784
+ - updated: The timestamp when the channel was last updated.
785
+
786
+ Args:
787
+ query: Filter and sort conditions. Supports operators like eq, neq, gt, gte, lt, lte,
788
+ in, like, fuzzy, keyword, not, and, or. Example: {"filter": {"eq": {"status": "active"}}}
789
+ limit: Maximum results to return (default 1000)
790
+ cursor: Pagination cursor from previous response's next_cursor
791
+ fields: Field paths to include in results. Each path is a list of keys for nested access.
792
+ Example: [["id"], ["user", "name"]] returns id and user.name fields.
793
+
794
+ Returns:
795
+ ChannelsSearchResult with hits (list of AirbyteSearchHit[ChannelsSearchData]) and pagination info
796
+
797
+ Raises:
798
+ NotImplementedError: If called in local execution mode
799
+ """
800
+ params: dict[str, Any] = {"query": query}
801
+ if limit is not None:
802
+ params["limit"] = limit
803
+ if cursor is not None:
804
+ params["cursor"] = cursor
805
+ if fields is not None:
806
+ params["fields"] = fields
807
+
808
+ result = await self._connector.execute("channels", "search", params)
809
+
810
+ # Parse response into typed result
811
+ return ChannelsSearchResult(
812
+ hits=[
813
+ AirbyteSearchHit[ChannelsSearchData](
814
+ id=hit.get("id"),
815
+ score=hit.get("score"),
816
+ data=ChannelsSearchData(**hit.get("data", {}))
817
+ )
818
+ for hit in result.get("hits", [])
819
+ ],
820
+ next_cursor=result.get("next_cursor"),
821
+ took_ms=result.get("took_ms")
822
+ )
823
+
653
824
  class ChannelMessagesQuery:
654
825
  """
655
826
  Query class for ChannelMessages entity operations.
@@ -27,7 +27,7 @@ from uuid import (
27
27
  SlackConnectorModel: ConnectorModel = ConnectorModel(
28
28
  id=UUID('c2281cee-86f9-4a86-bb48-d23286b4c7bd'),
29
29
  name='slack',
30
- version='0.1.7',
30
+ version='0.1.8',
31
31
  base_url='https://slack.com/api',
32
32
  auth=AuthConfig(
33
33
  options=[
@@ -184,27 +184,6 @@ class Reaction(BaseModel):
184
184
  users: Union[list[str] | None, Any] = Field(default=None)
185
185
  count: Union[int | None, Any] = Field(default=None)
186
186
 
187
- class Attachment(BaseModel):
188
- """Message attachment"""
189
- model_config = ConfigDict(extra="allow", populate_by_name=True)
190
-
191
- id: Union[int | None, Any] = Field(default=None)
192
- fallback: Union[str | None, Any] = Field(default=None)
193
- color: Union[str | None, Any] = Field(default=None)
194
- pretext: Union[str | None, Any] = Field(default=None)
195
- author_name: Union[str | None, Any] = Field(default=None)
196
- author_link: Union[str | None, Any] = Field(default=None)
197
- author_icon: Union[str | None, Any] = Field(default=None)
198
- title: Union[str | None, Any] = Field(default=None)
199
- title_link: Union[str | None, Any] = Field(default=None)
200
- text: Union[str | None, Any] = Field(default=None)
201
- fields: Union[list[dict[str, Any]] | None, Any] = Field(default=None)
202
- image_url: Union[str | None, Any] = Field(default=None)
203
- thumb_url: Union[str | None, Any] = Field(default=None)
204
- footer: Union[str | None, Any] = Field(default=None)
205
- footer_icon: Union[str | None, Any] = Field(default=None)
206
- ts: Union[Any, Any] = Field(default=None)
207
-
208
187
  class File(BaseModel):
209
188
  """File object"""
210
189
  model_config = ConfigDict(extra="allow", populate_by_name=True)
@@ -229,6 +208,27 @@ class File(BaseModel):
229
208
  created: Union[int | None, Any] = Field(default=None)
230
209
  timestamp: Union[int | None, Any] = Field(default=None)
231
210
 
211
+ class Attachment(BaseModel):
212
+ """Message attachment"""
213
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
214
+
215
+ id: Union[int | None, Any] = Field(default=None)
216
+ fallback: Union[str | None, Any] = Field(default=None)
217
+ color: Union[str | None, Any] = Field(default=None)
218
+ pretext: Union[str | None, Any] = Field(default=None)
219
+ author_name: Union[str | None, Any] = Field(default=None)
220
+ author_link: Union[str | None, Any] = Field(default=None)
221
+ author_icon: Union[str | None, Any] = Field(default=None)
222
+ title: Union[str | None, Any] = Field(default=None)
223
+ title_link: Union[str | None, Any] = Field(default=None)
224
+ text: Union[str | None, Any] = Field(default=None)
225
+ fields: Union[list[dict[str, Any]] | None, Any] = Field(default=None)
226
+ image_url: Union[str | None, Any] = Field(default=None)
227
+ thumb_url: Union[str | None, Any] = Field(default=None)
228
+ footer: Union[str | None, Any] = Field(default=None)
229
+ footer_icon: Union[str | None, Any] = Field(default=None)
230
+ ts: Union[Any, Any] = Field(default=None)
231
+
232
232
  class Message(BaseModel):
233
233
  """Slack message object"""
234
234
  model_config = ConfigDict(extra="allow", populate_by_name=True)
@@ -496,6 +496,166 @@ class SlackExecuteResultWithMeta(SlackExecuteResult[T], Generic[T, S]):
496
496
  meta: S
497
497
  """Metadata about the response (e.g., pagination cursors, record counts)."""
498
498
 
499
+ # ===== SEARCH DATA MODELS =====
500
+ # Entity-specific Pydantic models for search result data
501
+
502
+ # Type variable for search data generic
503
+ D = TypeVar('D')
504
+
505
+ class ChannelsSearchData(BaseModel):
506
+ """Search result data for channels entity."""
507
+ model_config = ConfigDict(extra="allow")
508
+
509
+ context_team_id: str | None = None
510
+ """The unique identifier of the team context in which the channel exists."""
511
+ created: int | None = None
512
+ """The timestamp when the channel was created."""
513
+ creator: str | None = None
514
+ """The ID of the user who created the channel."""
515
+ id: str | None = None
516
+ """The unique identifier of the channel."""
517
+ is_archived: bool | None = None
518
+ """Indicates if the channel is archived."""
519
+ is_channel: bool | None = None
520
+ """Indicates if the entity is a channel."""
521
+ is_ext_shared: bool | None = None
522
+ """Indicates if the channel is externally shared."""
523
+ is_general: bool | None = None
524
+ """Indicates if the channel is a general channel in the workspace."""
525
+ is_group: bool | None = None
526
+ """Indicates if the channel is a group (private channel) rather than a regular channel."""
527
+ is_im: bool | None = None
528
+ """Indicates if the entity is a direct message (IM) channel."""
529
+ is_member: bool | None = None
530
+ """Indicates if the calling user is a member of the channel."""
531
+ is_mpim: bool | None = None
532
+ """Indicates if the entity is a multiple person direct message (MPIM) channel."""
533
+ is_org_shared: bool | None = None
534
+ """Indicates if the channel is organization-wide shared."""
535
+ is_pending_ext_shared: bool | None = None
536
+ """Indicates if the channel is pending external shared."""
537
+ is_private: bool | None = None
538
+ """Indicates if the channel is a private channel."""
539
+ is_read_only: bool | None = None
540
+ """Indicates if the channel is read-only."""
541
+ is_shared: bool | None = None
542
+ """Indicates if the channel is shared."""
543
+ last_read: str | None = None
544
+ """The timestamp of the user's last read message in the channel."""
545
+ locale: str | None = None
546
+ """The locale of the channel."""
547
+ name: str | None = None
548
+ """The name of the channel."""
549
+ name_normalized: str | None = None
550
+ """The normalized name of the channel."""
551
+ num_members: int | None = None
552
+ """The number of members in the channel."""
553
+ parent_conversation: str | None = None
554
+ """The parent conversation of the channel."""
555
+ pending_connected_team_ids: list[Any] | None = None
556
+ """The IDs of teams that are pending to be connected to the channel."""
557
+ pending_shared: list[Any] | None = None
558
+ """The list of pending shared items of the channel."""
559
+ previous_names: list[Any] | None = None
560
+ """The previous names of the channel."""
561
+ purpose: dict[str, Any] | None = None
562
+ """The purpose of the channel."""
563
+ shared_team_ids: list[Any] | None = None
564
+ """The IDs of teams with which the channel is shared."""
565
+ topic: dict[str, Any] | None = None
566
+ """The topic of the channel."""
567
+ unlinked: int | None = None
568
+ """Indicates if the channel is unlinked."""
569
+ updated: int | None = None
570
+ """The timestamp when the channel was last updated."""
571
+
572
+
573
+ class UsersSearchData(BaseModel):
574
+ """Search result data for users entity."""
575
+ model_config = ConfigDict(extra="allow")
576
+
577
+ color: str | None = None
578
+ """The color assigned to the user for visual purposes."""
579
+ deleted: bool | None = None
580
+ """Indicates if the user is deleted or not."""
581
+ has_2fa: bool | None = None
582
+ """Flag indicating if the user has two-factor authentication enabled."""
583
+ id: str | None = None
584
+ """Unique identifier for the user."""
585
+ is_admin: bool | None = None
586
+ """Flag specifying if the user is an admin or not."""
587
+ is_app_user: bool | None = None
588
+ """Specifies if the user is an app user."""
589
+ is_bot: bool | None = None
590
+ """Indicates if the user is a bot account."""
591
+ is_email_confirmed: bool | None = None
592
+ """Flag indicating if the user's email is confirmed."""
593
+ is_forgotten: bool | None = None
594
+ """Specifies if the user is marked as forgotten."""
595
+ is_invited_user: bool | None = None
596
+ """Indicates if the user is invited or not."""
597
+ is_owner: bool | None = None
598
+ """Flag indicating if the user is an owner."""
599
+ is_primary_owner: bool | None = None
600
+ """Specifies if the user is the primary owner."""
601
+ is_restricted: bool | None = None
602
+ """Flag specifying if the user is restricted."""
603
+ is_ultra_restricted: bool | None = None
604
+ """Indicates if the user has ultra-restricted access."""
605
+ name: str | None = None
606
+ """The username of the user."""
607
+ profile: dict[str, Any] | None = None
608
+ """User's profile information containing detailed details."""
609
+ real_name: str | None = None
610
+ """The real name of the user."""
611
+ team_id: str | None = None
612
+ """Unique identifier for the team the user belongs to."""
613
+ tz: str | None = None
614
+ """Timezone of the user."""
615
+ tz_label: str | None = None
616
+ """Label representing the timezone of the user."""
617
+ tz_offset: int | None = None
618
+ """Offset of the user's timezone."""
619
+ updated: int | None = None
620
+ """Timestamp of when the user's information was last updated."""
621
+ who_can_share_contact_card: str | None = None
622
+ """Specifies who can share the user's contact card."""
623
+
624
+
625
+ # ===== GENERIC SEARCH RESULT TYPES =====
626
+
627
+ class AirbyteSearchHit(BaseModel, Generic[D]):
628
+ """A single search result with typed data."""
629
+ model_config = ConfigDict(extra="allow")
630
+
631
+ id: str | None = None
632
+ """Unique identifier for the record."""
633
+ score: float | None = None
634
+ """Relevance score for the match."""
635
+ data: D
636
+ """The matched record data."""
637
+
638
+
639
+ class AirbyteSearchResult(BaseModel, Generic[D]):
640
+ """Result from Airbyte cache search operations with typed hits."""
641
+ model_config = ConfigDict(extra="allow")
642
+
643
+ hits: list[AirbyteSearchHit[D]] = Field(default_factory=list)
644
+ """List of matching records."""
645
+ next_cursor: str | None = None
646
+ """Cursor for fetching the next page of results."""
647
+ took_ms: int | None = None
648
+ """Time taken to execute the search in milliseconds."""
649
+
650
+
651
+ # ===== ENTITY-SPECIFIC SEARCH RESULT TYPE ALIASES =====
652
+
653
+ ChannelsSearchResult = AirbyteSearchResult[ChannelsSearchData]
654
+ """Search result type for channels entity."""
655
+
656
+ UsersSearchResult = AirbyteSearchResult[UsersSearchData]
657
+ """Search result type for users entity."""
658
+
499
659
 
500
660
 
501
661
  # ===== OPERATION RESULT TYPE ALIASES =====