linkedapi 1.3.10__tar.gz → 1.3.11__tar.gz

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 (110) hide show
  1. {linkedapi-1.3.10 → linkedapi-1.3.11}/PKG-INFO +1 -1
  2. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/__init__.py +3 -1
  3. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/client.py +3 -0
  4. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/errors.py +2 -0
  5. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/__init__.py +2 -0
  6. linkedapi-1.3.11/linkedapi/operations/search_posts.py +15 -0
  7. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/__init__.py +26 -0
  8. linkedapi-1.3.11/linkedapi/types/search_posts.py +103 -0
  9. {linkedapi-1.3.10 → linkedapi-1.3.11}/pyproject.toml +1 -1
  10. {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_errors.py +1 -0
  11. {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_operations.py +157 -1
  12. {linkedapi-1.3.10 → linkedapi-1.3.11}/.github/workflows/publish.yaml +0 -0
  13. {linkedapi-1.3.10 → linkedapi-1.3.11}/.gitignore +0 -0
  14. {linkedapi-1.3.10 → linkedapi-1.3.11}/LICENSE +0 -0
  15. {linkedapi-1.3.10 → linkedapi-1.3.11}/README.md +0 -0
  16. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/__init__.py +0 -0
  17. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/accounts.py +0 -0
  18. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/admin.py +0 -0
  19. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/http_client.py +0 -0
  20. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/limits.py +0 -0
  21. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/subscription.py +0 -0
  22. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/webhooks.py +0 -0
  23. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/config.py +0 -0
  24. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/core/__init__.py +0 -0
  25. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/core/operation.py +0 -0
  26. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/core/polling.py +0 -0
  27. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/http/__init__.py +0 -0
  28. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/http/base.py +0 -0
  29. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/http/linked_api_http_client.py +0 -0
  30. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/__init__.py +0 -0
  31. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/array.py +0 -0
  32. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/base.py +0 -0
  33. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/send_message.py +0 -0
  34. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/simple.py +0 -0
  35. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/then.py +0 -0
  36. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/void.py +0 -0
  37. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/accept_invitation.py +0 -0
  38. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/check_connection_status.py +0 -0
  39. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/comment_on_post.py +0 -0
  40. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/create_post.py +0 -0
  41. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/custom_workflow.py +0 -0
  42. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/fetch_company.py +0 -0
  43. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/fetch_job.py +0 -0
  44. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/fetch_person.py +0 -0
  45. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/fetch_post.py +0 -0
  46. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/ignore_invitation.py +0 -0
  47. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/manage_conversation.py +0 -0
  48. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_fetch_company.py +0 -0
  49. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_fetch_person.py +0 -0
  50. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_manage_conversation.py +0 -0
  51. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_search_companies.py +0 -0
  52. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_search_people.py +0 -0
  53. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_send_message.py +0 -0
  54. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_sync_conversation.py +0 -0
  55. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_sync_inbox.py +0 -0
  56. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/react_to_comment.py +0 -0
  57. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/react_to_post.py +0 -0
  58. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/remove_connection.py +0 -0
  59. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/reply_to_comment.py +0 -0
  60. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_connections.py +0 -0
  61. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_feed.py +0 -0
  62. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_invitations.py +0 -0
  63. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_pending_requests.py +0 -0
  64. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_performance.py +0 -0
  65. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_profile_viewers.py +0 -0
  66. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_ssi.py +0 -0
  67. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/search_companies.py +0 -0
  68. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/search_jobs.py +0 -0
  69. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/search_people.py +0 -0
  70. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/send_connection_request.py +0 -0
  71. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/send_message.py +0 -0
  72. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/sync_conversation.py +0 -0
  73. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/sync_inbox.py +0 -0
  74. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/sync_network.py +0 -0
  75. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/withdraw_connection_request.py +0 -0
  76. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/py.typed +0 -0
  77. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/account.py +0 -0
  78. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/admin/__init__.py +0 -0
  79. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/admin/accounts.py +0 -0
  80. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/admin/config.py +0 -0
  81. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/admin/limits.py +0 -0
  82. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/admin/subscription.py +0 -0
  83. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/base.py +0 -0
  84. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/company.py +0 -0
  85. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/connection.py +0 -0
  86. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/feed.py +0 -0
  87. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/jobs.py +0 -0
  88. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/message.py +0 -0
  89. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/network.py +0 -0
  90. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/params.py +0 -0
  91. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/person.py +0 -0
  92. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/post.py +0 -0
  93. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/profile_viewers.py +0 -0
  94. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/responses.py +0 -0
  95. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/search_companies.py +0 -0
  96. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/search_people.py +0 -0
  97. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/statistics.py +0 -0
  98. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/webhooks.py +0 -0
  99. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/workflow.py +0 -0
  100. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/webhooks/__init__.py +0 -0
  101. {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/webhooks/parse.py +0 -0
  102. {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/conftest.py +0 -0
  103. {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_admin.py +0 -0
  104. {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_direct_methods.py +0 -0
  105. {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_http_client.py +0 -0
  106. {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_mappers.py +0 -0
  107. {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_models.py +0 -0
  108. {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_polling.py +0 -0
  109. {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_webhooks.py +0 -0
  110. {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_working_hours_contract.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: linkedapi
3
- Version: 1.3.10
3
+ Version: 1.3.11
4
4
  Summary: Official synchronous Python SDK for Linked API.
5
5
  Project-URL: Homepage, https://linkedapi.io
6
6
  Project-URL: Repository, https://github.com/Linked-API/linkedapi-python
@@ -68,6 +68,7 @@ from linkedapi.operations import (
68
68
  SearchCompanies,
69
69
  SearchJobs,
70
70
  SearchPeople,
71
+ SearchPosts,
71
72
  SendConnectionRequest,
72
73
  SendMessage,
73
74
  SyncConversation,
@@ -79,7 +80,7 @@ from linkedapi.types import * # noqa: F403
79
80
  from linkedapi.types import __all__ as _types_all
80
81
  from linkedapi.webhooks import parse_webhook_event
81
82
 
82
- __version__ = "1.3.9"
83
+ __version__ = "1.3.11"
83
84
  PredefinedOperation = Operation
84
85
 
85
86
  __all__ = [
@@ -146,6 +147,7 @@ __all__ = [
146
147
  "SearchCompanies",
147
148
  "SearchJobs",
148
149
  "SearchPeople",
150
+ "SearchPosts",
149
151
  "SendConnectionRequest",
150
152
  "SendMessage",
151
153
  "SimpleWorkflowMapper",
@@ -41,6 +41,7 @@ from linkedapi.operations import (
41
41
  SearchCompanies,
42
42
  SearchJobs,
43
43
  SearchPeople,
44
+ SearchPosts,
44
45
  SendConnectionRequest,
45
46
  SendMessage,
46
47
  SyncConversation,
@@ -87,6 +88,7 @@ class LinkedApi:
87
88
  self.search_companies = SearchCompanies(self.http_client)
88
89
  self.search_people = SearchPeople(self.http_client)
89
90
  self.search_jobs = SearchJobs(self.http_client)
91
+ self.search_posts = SearchPosts(self.http_client)
90
92
  self.fetch_company = FetchCompany(self.http_client)
91
93
  self.fetch_person = FetchPerson(self.http_client)
92
94
  self.fetch_post = FetchPost(self.http_client)
@@ -128,6 +130,7 @@ class LinkedApi:
128
130
  self.search_companies,
129
131
  self.search_people,
130
132
  self.search_jobs,
133
+ self.search_posts,
131
134
  self.fetch_company,
132
135
  self.fetch_person,
133
136
  self.fetch_post,
@@ -17,6 +17,7 @@ LinkedApiActionErrorType = Literal[
17
17
  "connectionNotFound",
18
18
  "searchingNotAllowed",
19
19
  "searchInterfaceMismatch",
20
+ "filterIdentityMismatch",
20
21
  "companyNotFound",
21
22
  "postNotFound",
22
23
  "jobNotFound",
@@ -43,6 +44,7 @@ LINKED_API_ACTION_ERROR_TYPES: tuple[str, ...] = (
43
44
  "connectionNotFound",
44
45
  "searchingNotAllowed",
45
46
  "searchInterfaceMismatch",
47
+ "filterIdentityMismatch",
46
48
  "companyNotFound",
47
49
  "postNotFound",
48
50
  "jobNotFound",
@@ -31,6 +31,7 @@ from linkedapi.operations.retrieve_ssi import RetrieveSSI
31
31
  from linkedapi.operations.search_companies import SearchCompanies
32
32
  from linkedapi.operations.search_jobs import SearchJobs
33
33
  from linkedapi.operations.search_people import SearchPeople
34
+ from linkedapi.operations.search_posts import SearchPosts
34
35
  from linkedapi.operations.send_connection_request import SendConnectionRequest
35
36
  from linkedapi.operations.send_message import SendMessage
36
37
  from linkedapi.operations.sync_conversation import SyncConversation
@@ -78,6 +79,7 @@ __all__ = [
78
79
  "SearchCompanies",
79
80
  "SearchJobs",
80
81
  "SearchPeople",
82
+ "SearchPosts",
81
83
  "SendConnectionRequest",
82
84
  "SendMessage",
83
85
  "SyncConversation",
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from linkedapi.core import Operation
4
+ from linkedapi.mappers import ArrayWorkflowMapper
5
+ from linkedapi.types import SearchPostResult, SearchPostsParams
6
+
7
+
8
+ class SearchPosts(Operation[SearchPostsParams, list[SearchPostResult]]):
9
+ """Search posts on standard LinkedIn."""
10
+
11
+ operation_name = "searchPosts"
12
+ mapper = ArrayWorkflowMapper[SearchPostsParams, SearchPostResult](
13
+ "st.searchPosts",
14
+ SearchPostResult,
15
+ )
@@ -206,6 +206,20 @@ from linkedapi.types.search_people import (
206
206
  SearchPeopleParams,
207
207
  SearchPeopleResult,
208
208
  )
209
+ from linkedapi.types.search_posts import (
210
+ PostSearchContentType,
211
+ PostSearchDatePosted,
212
+ PostSearchPostedBy,
213
+ PostSearchSort,
214
+ SearchPostActor,
215
+ SearchPostResult,
216
+ SearchPostsCompanyFilter,
217
+ SearchPostsCompanyFilterEntry,
218
+ SearchPostsFilter,
219
+ SearchPostsParams,
220
+ SearchPostsPersonFilter,
221
+ SearchPostsPersonFilterEntry,
222
+ )
209
223
  from linkedapi.types.statistics import (
210
224
  ApiUsageAction,
211
225
  ApiUsageParams,
@@ -392,6 +406,10 @@ __all__ = [
392
406
  "PostReaction",
393
407
  "PostReactionsRetrievalConfig",
394
408
  "PostReposter",
409
+ "PostSearchContentType",
410
+ "PostSearchDatePosted",
411
+ "PostSearchPostedBy",
412
+ "PostSearchSort",
395
413
  "PostType",
396
414
  "ProfileViewer",
397
415
  "ProfileViewerType",
@@ -428,6 +446,14 @@ __all__ = [
428
446
  "SearchPeopleFilter",
429
447
  "SearchPeopleParams",
430
448
  "SearchPeopleResult",
449
+ "SearchPostActor",
450
+ "SearchPostResult",
451
+ "SearchPostsCompanyFilter",
452
+ "SearchPostsCompanyFilterEntry",
453
+ "SearchPostsFilter",
454
+ "SearchPostsParams",
455
+ "SearchPostsPersonFilter",
456
+ "SearchPostsPersonFilterEntry",
431
457
  "SeatType",
432
458
  "SendConnectionRequestParams",
433
459
  "SendMessageManageConversation",
@@ -0,0 +1,103 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal, TypeAlias
4
+
5
+ from linkedapi.types.base import LinkedApiModel
6
+ from linkedapi.types.params import BaseActionParams, LimitParams
7
+ from linkedapi.types.post import PostActorType, PostType
8
+
9
+ PostSearchSort = Literal["topMatch", "latest"]
10
+ PostSearchDatePosted = Literal["past24Hours", "pastWeek", "pastMonth"]
11
+ PostSearchContentType = Literal["videos", "images", "jobPosts", "liveVideos", "documents"]
12
+ PostSearchPostedBy = Literal["me", "firstConnections", "peopleYouFollow"]
13
+
14
+
15
+ class SearchPostsPersonFilter(LinkedApiModel):
16
+ """One person to narrow a post search by.
17
+
18
+ "name" is always required because LinkedIn's filter panel accepts nothing but typed text. The
19
+ optional identifier decides which of the offered namesakes is taken; with "name" alone the pick
20
+ is whichever suggestion LinkedIn ranked first.
21
+ """
22
+
23
+ name: str
24
+ urn: str | None = None
25
+ person_hashed_url: str | None = None
26
+
27
+
28
+ class SearchPostsCompanyFilter(LinkedApiModel):
29
+ """One company to narrow a post search by. Same contract as SearchPostsPersonFilter."""
30
+
31
+ name: str
32
+ urn: str | None = None
33
+ company_hashed_url: str | None = None
34
+
35
+
36
+ # A plain string is shorthand for the name alone: "Bill Gates" means {"name": "Bill Gates"}.
37
+ # Both forms may be mixed in one list.
38
+ SearchPostsPersonFilterEntry: TypeAlias = SearchPostsPersonFilter | str
39
+ SearchPostsCompanyFilterEntry: TypeAlias = SearchPostsCompanyFilter | str
40
+
41
+
42
+ class SearchPostsFilter(LinkedApiModel):
43
+ """Filtering criteria for the LinkedIn content search.
44
+
45
+ Every specified field is applied, or the action fails. Ignored entirely when
46
+ "custom_search_url" is specified.
47
+ """
48
+
49
+ sort: PostSearchSort | None = None
50
+ date_posted: PostSearchDatePosted | None = None
51
+ content_type: PostSearchContentType | None = None
52
+ posted_by: list[PostSearchPostedBy] | None = None
53
+ from_members: list[SearchPostsPersonFilterEntry] | None = None
54
+ from_companies: list[SearchPostsCompanyFilterEntry] | None = None
55
+ mentioning_members: list[SearchPostsPersonFilterEntry] | None = None
56
+ mentioning_companies: list[SearchPostsCompanyFilterEntry] | None = None
57
+ author_companies: list[SearchPostsCompanyFilterEntry] | None = None
58
+ author_industries: list[str] | None = None
59
+
60
+
61
+ class SearchPostsParams(BaseActionParams, LimitParams):
62
+ # Either "term" or "custom_search_url" must be provided. "limit" defaults to 10 and may go up
63
+ # to 100, or up to 20 when child actions are attached.
64
+ term: str | None = None
65
+ filter: SearchPostsFilter | None = None
66
+ custom_search_url: str | None = None
67
+
68
+
69
+ class SearchPostActor(LinkedApiModel):
70
+ """Whoever a search result attributes content to: its author, or the actor that reshared it.
71
+
72
+ Unlike the actors of a post returned by fetch_post, these carry no "urn": the URN is read from
73
+ identity anchors on the post's own page, and a search result list has none of them.
74
+ """
75
+
76
+ type: PostActorType | None = None
77
+ name: str | None = None
78
+ profile_url: str | None = None
79
+ headline: str | None = None
80
+ company_url: str | None = None
81
+
82
+
83
+ class SearchPostResult(LinkedApiModel):
84
+ url: str | None = None
85
+ activity_urn: str | None = None
86
+ time: str | None = None
87
+ type: PostType | None = None
88
+ author: SearchPostActor | None = None
89
+ # Non-null only when "type" is "repost".
90
+ reposter: SearchPostActor | None = None
91
+ text: str | None = None
92
+ repost_text: str | None = None
93
+ hashtags: list[str] | None = None
94
+ mentions: list[str] | None = None
95
+ external_links: list[str] | None = None
96
+ images: list[str] | None = None
97
+ document_slides: list[str] | None = None
98
+ has_video: bool | None = None
99
+ video_thumbnail: str | None = None
100
+ has_poll: bool | None = None
101
+ reactions_count: int | None = None
102
+ comments_count: int | None = None
103
+ reposts_count: int | None = None
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "linkedapi"
7
- version = "1.3.10"
7
+ version = "1.3.11"
8
8
  description = "Official synchronous Python SDK for Linked API."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -46,6 +46,7 @@ def test_error_type_sets_match_node_contract() -> None:
46
46
  "connectionNotFound",
47
47
  "searchingNotAllowed",
48
48
  "searchInterfaceMismatch",
49
+ "filterIdentityMismatch",
49
50
  "companyNotFound",
50
51
  "postNotFound",
51
52
  "jobNotFound",
@@ -13,6 +13,12 @@ from linkedapi import (
13
13
  LinkedApi,
14
14
  LinkedApiConfig,
15
15
  RetrieveFeedParams,
16
+ SearchPostActor,
17
+ SearchPostResult,
18
+ SearchPostsCompanyFilter,
19
+ SearchPostsFilter,
20
+ SearchPostsParams,
21
+ SearchPostsPersonFilter,
16
22
  SimpleWorkflowMapper,
17
23
  VoidWorkflowMapper,
18
24
  )
@@ -33,6 +39,7 @@ def test_linked_api_exposes_all_predefined_operations() -> None:
33
39
  "search_companies",
34
40
  "nv_search_companies",
35
41
  "search_jobs",
42
+ "search_posts",
36
43
  "send_connection_request",
37
44
  "check_connection_status",
38
45
  "withdraw_connection_request",
@@ -67,7 +74,7 @@ def test_linked_api_exposes_all_predefined_operations() -> None:
67
74
  assert hasattr(operation, "execute")
68
75
  assert hasattr(operation, "result")
69
76
  assert hasattr(operation, "cancel")
70
- assert len(linkedapi.operations) == 39
77
+ assert len(linkedapi.operations) == 40
71
78
 
72
79
 
73
80
  def test_operation_mappers_match_node_contract() -> None:
@@ -79,6 +86,8 @@ def test_operation_mappers_match_node_contract() -> None:
79
86
  assert linkedapi.search_people.mapper.base_action_type == "st.searchPeople"
80
87
  assert isinstance(linkedapi.search_jobs.mapper, ArrayWorkflowMapper)
81
88
  assert linkedapi.search_jobs.mapper.base_action_type == "st.searchJobs"
89
+ assert isinstance(linkedapi.search_posts.mapper, ArrayWorkflowMapper)
90
+ assert linkedapi.search_posts.mapper.base_action_type == "st.searchPosts"
82
91
  assert linkedapi.fetch_job.mapper.base_action_type == "st.openJob"
83
92
  assert linkedapi.fetch_job.mapper.default_params == {"basicInfo": True}
84
93
  assert isinstance(linkedapi.send_connection_request.mapper, VoidWorkflowMapper)
@@ -134,6 +143,153 @@ def test_retrieve_feed_maps_params_and_feed_context() -> None:
134
143
  RetrieveFeedParams(limit=invalid_limit)
135
144
 
136
145
 
146
+ def test_search_posts_maps_filter_params_and_actors_without_urn() -> None:
147
+ linkedapi = LinkedApi(LinkedApiConfig(linked_api_token="x", identification_token="y"))
148
+
149
+ request = linkedapi.search_posts.mapper.map_request(
150
+ SearchPostsParams(
151
+ term="climate tech",
152
+ limit=20,
153
+ filter={
154
+ "sort": "latest",
155
+ "date_posted": "pastWeek",
156
+ "content_type": "images",
157
+ "posted_by": ["firstConnections", "peopleYouFollow"],
158
+ "from_members": [{"name": "Bill Gates", "urn": "urn:li:member:251749025"}],
159
+ "from_companies": ["Example Company"],
160
+ "mentioning_members": [],
161
+ "mentioning_companies": [],
162
+ "author_companies": [
163
+ {"name": "Example Company", "urn": "urn:li:organization:1234567"}
164
+ ],
165
+ "author_industries": ["Software Development"],
166
+ },
167
+ custom_search_url=(
168
+ "https://www.linkedin.com/search/results/content/?keywords=climate%20tech"
169
+ ),
170
+ )
171
+ )
172
+
173
+ assert request == {
174
+ "actionType": "st.searchPosts",
175
+ "term": "climate tech",
176
+ "limit": 20,
177
+ "filter": {
178
+ "sort": "latest",
179
+ "datePosted": "pastWeek",
180
+ "contentType": "images",
181
+ "postedBy": ["firstConnections", "peopleYouFollow"],
182
+ "fromMembers": [{"name": "Bill Gates", "urn": "urn:li:member:251749025"}],
183
+ "fromCompanies": ["Example Company"],
184
+ "mentioningMembers": [],
185
+ "mentioningCompanies": [],
186
+ "authorCompanies": [{"name": "Example Company", "urn": "urn:li:organization:1234567"}],
187
+ "authorIndustries": ["Software Development"],
188
+ },
189
+ "customSearchUrl": "https://www.linkedin.com/search/results/content/?keywords=climate%20tech",
190
+ }
191
+
192
+ response = linkedapi.search_posts.mapper.map_response(
193
+ {
194
+ "actionType": "st.searchPosts",
195
+ "success": True,
196
+ "data": [
197
+ {
198
+ "url": "https://www.linkedin.com/feed/update/urn:li:activity:1",
199
+ "activityUrn": "urn:li:activity:1",
200
+ "time": "2023-01-01T09:15:00Z",
201
+ "type": "repost",
202
+ "author": {
203
+ "type": "company",
204
+ "name": "Example Company",
205
+ "companyUrl": "https://www.linkedin.com/company/example-company",
206
+ },
207
+ "reposter": {
208
+ "type": "person",
209
+ "name": "Example Reposter",
210
+ "profileUrl": "https://www.linkedin.com/in/example-reposter",
211
+ "headline": None,
212
+ },
213
+ "text": "Original post content about the webinar.",
214
+ "repostText": "A useful summary for anyone planning a launch.",
215
+ "hashtags": [],
216
+ "mentions": [],
217
+ "externalLinks": [],
218
+ "images": [],
219
+ "documentSlides": [],
220
+ "hasVideo": True,
221
+ "videoThumbnail": "https://media.licdn.com/dms/image/video-cover.jpg",
222
+ "hasPoll": False,
223
+ "reactionsCount": 6,
224
+ "commentsCount": 0,
225
+ "repostsCount": 1,
226
+ }
227
+ ],
228
+ }
229
+ )
230
+
231
+ assert response.data is not None
232
+ post = response.data[0]
233
+ assert isinstance(post, SearchPostResult)
234
+ assert post.activity_urn == "urn:li:activity:1"
235
+ assert post.author is not None
236
+ assert post.author.company_url == "https://www.linkedin.com/company/example-company"
237
+ assert post.reposter is not None
238
+ assert post.reposter.profile_url == "https://www.linkedin.com/in/example-reposter"
239
+ assert post.video_thumbnail == "https://media.licdn.com/dms/image/video-cover.jpg"
240
+ assert post.reposts_count == 1
241
+ # A search result never resolves an actor urn, so the field must not exist on this surface.
242
+ assert "urn" not in SearchPostActor.model_fields
243
+
244
+
245
+ def test_search_posts_actor_filters_accept_bare_strings_and_objects_mixed() -> None:
246
+ params = SearchPostsParams(
247
+ term="ai",
248
+ filter=SearchPostsFilter(
249
+ from_members=[
250
+ "Bill Gates",
251
+ SearchPostsPersonFilter(
252
+ name="Satya Nadella",
253
+ person_hashed_url="https://www.linkedin.com/in/ACoAAABC",
254
+ ),
255
+ ],
256
+ from_companies=[
257
+ "Acme",
258
+ SearchPostsCompanyFilter(
259
+ name="Globex",
260
+ company_hashed_url="https://www.linkedin.com/company/1035",
261
+ ),
262
+ ],
263
+ ),
264
+ )
265
+
266
+ assert params.model_dump(by_alias=True, exclude_none=True) == {
267
+ "term": "ai",
268
+ "filter": {
269
+ "fromMembers": [
270
+ "Bill Gates",
271
+ {
272
+ "name": "Satya Nadella",
273
+ "personHashedUrl": "https://www.linkedin.com/in/ACoAAABC",
274
+ },
275
+ ],
276
+ "fromCompanies": [
277
+ "Acme",
278
+ {
279
+ "name": "Globex",
280
+ "companyHashedUrl": "https://www.linkedin.com/company/1035",
281
+ },
282
+ ],
283
+ },
284
+ }
285
+
286
+ with pytest.raises(ValidationError):
287
+ SearchPostsFilter(sort="mostRecent")
288
+
289
+ with pytest.raises(ValidationError):
290
+ SearchPostsFilter(from_members=[{"urn": "urn:li:member:1"}])
291
+
292
+
137
293
  def test_invitation_params_require_the_matching_target_url() -> None:
138
294
  params = AcceptInvitationParams(
139
295
  invitation_type="companyFollow",
File without changes
File without changes
File without changes
File without changes