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.
- {linkedapi-1.3.10 → linkedapi-1.3.11}/PKG-INFO +1 -1
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/__init__.py +3 -1
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/client.py +3 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/errors.py +2 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/__init__.py +2 -0
- linkedapi-1.3.11/linkedapi/operations/search_posts.py +15 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/__init__.py +26 -0
- linkedapi-1.3.11/linkedapi/types/search_posts.py +103 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/pyproject.toml +1 -1
- {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_errors.py +1 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_operations.py +157 -1
- {linkedapi-1.3.10 → linkedapi-1.3.11}/.github/workflows/publish.yaml +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/.gitignore +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/LICENSE +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/README.md +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/__init__.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/accounts.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/admin.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/http_client.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/limits.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/subscription.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/admin/webhooks.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/config.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/core/__init__.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/core/operation.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/core/polling.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/http/__init__.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/http/base.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/http/linked_api_http_client.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/__init__.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/array.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/base.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/send_message.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/simple.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/then.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/mappers/void.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/accept_invitation.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/check_connection_status.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/comment_on_post.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/create_post.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/custom_workflow.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/fetch_company.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/fetch_job.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/fetch_person.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/fetch_post.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/ignore_invitation.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/manage_conversation.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_fetch_company.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_fetch_person.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_manage_conversation.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_search_companies.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_search_people.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_send_message.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_sync_conversation.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/nv_sync_inbox.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/react_to_comment.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/react_to_post.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/remove_connection.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/reply_to_comment.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_connections.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_feed.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_invitations.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_pending_requests.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_performance.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_profile_viewers.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/retrieve_ssi.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/search_companies.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/search_jobs.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/search_people.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/send_connection_request.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/send_message.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/sync_conversation.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/sync_inbox.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/sync_network.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/operations/withdraw_connection_request.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/py.typed +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/account.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/admin/__init__.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/admin/accounts.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/admin/config.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/admin/limits.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/admin/subscription.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/base.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/company.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/connection.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/feed.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/jobs.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/message.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/network.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/params.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/person.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/post.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/profile_viewers.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/responses.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/search_companies.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/search_people.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/statistics.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/webhooks.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/types/workflow.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/webhooks/__init__.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/linkedapi/webhooks/parse.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/conftest.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_admin.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_direct_methods.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_http_client.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_mappers.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_models.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_polling.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_webhooks.py +0 -0
- {linkedapi-1.3.10 → linkedapi-1.3.11}/tests/test_working_hours_contract.py +0 -0
|
@@ -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.
|
|
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
|
|
@@ -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) ==
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|