linkedapi 1.3.4__tar.gz → 1.3.6__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.4 → linkedapi-1.3.6}/PKG-INFO +1 -1
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/__init__.py +5 -1
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/client.py +6 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/core/operation.py +1 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/errors.py +8 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/__init__.py +4 -0
- linkedapi-1.3.6/linkedapi/operations/comment_on_post.py +15 -0
- linkedapi-1.3.6/linkedapi/operations/react_to_comment.py +12 -0
- linkedapi-1.3.6/linkedapi/operations/reply_to_comment.py +15 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/__init__.py +12 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/post.py +23 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/workflow.py +5 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/pyproject.toml +1 -1
- {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_errors.py +4 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_operations.py +10 -1
- linkedapi-1.3.6/tests/test_working_hours_contract.py +108 -0
- linkedapi-1.3.4/linkedapi/operations/comment_on_post.py +0 -12
- {linkedapi-1.3.4 → linkedapi-1.3.6}/.github/workflows/publish.yaml +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/.gitignore +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/LICENSE +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/README.md +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/__init__.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/accounts.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/admin.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/http_client.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/limits.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/subscription.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/webhooks.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/config.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/core/__init__.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/core/polling.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/http/__init__.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/http/base.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/http/linked_api_http_client.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/__init__.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/array.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/base.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/send_message.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/simple.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/then.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/void.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/accept_invitation.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/check_connection_status.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/create_post.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/custom_workflow.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/fetch_company.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/fetch_job.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/fetch_person.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/fetch_post.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/ignore_invitation.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/manage_conversation.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_fetch_company.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_fetch_person.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_manage_conversation.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_search_companies.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_search_people.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_send_message.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_sync_conversation.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_sync_inbox.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/react_to_post.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/remove_connection.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_connections.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_feed.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_invitations.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_pending_requests.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_performance.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_ssi.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/search_companies.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/search_jobs.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/search_people.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/send_connection_request.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/send_message.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/sync_conversation.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/sync_inbox.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/sync_network.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/withdraw_connection_request.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/py.typed +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/account.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/admin/__init__.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/admin/accounts.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/admin/config.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/admin/limits.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/admin/subscription.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/base.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/company.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/connection.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/feed.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/jobs.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/message.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/network.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/params.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/person.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/responses.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/search_companies.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/search_people.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/statistics.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/webhooks.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/webhooks/__init__.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/webhooks/parse.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/conftest.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_admin.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_direct_methods.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_http_client.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_mappers.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_models.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_polling.py +0 -0
- {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_webhooks.py +0 -0
|
@@ -54,8 +54,10 @@ from linkedapi.operations import (
|
|
|
54
54
|
NvSendMessage,
|
|
55
55
|
NvSyncConversation,
|
|
56
56
|
NvSyncInbox,
|
|
57
|
+
ReactToComment,
|
|
57
58
|
ReactToPost,
|
|
58
59
|
RemoveConnection,
|
|
60
|
+
ReplyToComment,
|
|
59
61
|
RetrieveConnections,
|
|
60
62
|
RetrieveFeed,
|
|
61
63
|
RetrieveInvitations,
|
|
@@ -76,7 +78,7 @@ from linkedapi.types import * # noqa: F403
|
|
|
76
78
|
from linkedapi.types import __all__ as _types_all
|
|
77
79
|
from linkedapi.webhooks import parse_webhook_event
|
|
78
80
|
|
|
79
|
-
__version__ = "1.3.
|
|
81
|
+
__version__ = "1.3.6"
|
|
80
82
|
PredefinedOperation = Operation
|
|
81
83
|
|
|
82
84
|
__all__ = [
|
|
@@ -128,8 +130,10 @@ __all__ = [
|
|
|
128
130
|
"NvSyncInbox",
|
|
129
131
|
"Operation",
|
|
130
132
|
"PredefinedOperation",
|
|
133
|
+
"ReactToComment",
|
|
131
134
|
"ReactToPost",
|
|
132
135
|
"RemoveConnection",
|
|
136
|
+
"ReplyToComment",
|
|
133
137
|
"ResponseMapping",
|
|
134
138
|
"RetrieveInvitations",
|
|
135
139
|
"RetrieveConnections",
|
|
@@ -27,8 +27,10 @@ from linkedapi.operations import (
|
|
|
27
27
|
NvSendMessage,
|
|
28
28
|
NvSyncConversation,
|
|
29
29
|
NvSyncInbox,
|
|
30
|
+
ReactToComment,
|
|
30
31
|
ReactToPost,
|
|
31
32
|
RemoveConnection,
|
|
33
|
+
ReplyToComment,
|
|
32
34
|
RetrieveConnections,
|
|
33
35
|
RetrieveFeed,
|
|
34
36
|
RetrieveInvitations,
|
|
@@ -90,6 +92,8 @@ class LinkedApi:
|
|
|
90
92
|
self.fetch_job = FetchJob(self.http_client)
|
|
91
93
|
self.react_to_post = ReactToPost(self.http_client)
|
|
92
94
|
self.comment_on_post = CommentOnPost(self.http_client)
|
|
95
|
+
self.react_to_comment = ReactToComment(self.http_client)
|
|
96
|
+
self.reply_to_comment = ReplyToComment(self.http_client)
|
|
93
97
|
self.create_post = CreatePost(self.http_client)
|
|
94
98
|
self.retrieve_feed = RetrieveFeed(self.http_client)
|
|
95
99
|
self.retrieve_ssi = RetrieveSSI(self.http_client)
|
|
@@ -128,6 +132,8 @@ class LinkedApi:
|
|
|
128
132
|
self.fetch_job,
|
|
129
133
|
self.react_to_post,
|
|
130
134
|
self.comment_on_post,
|
|
135
|
+
self.react_to_comment,
|
|
136
|
+
self.reply_to_comment,
|
|
131
137
|
self.create_post,
|
|
132
138
|
self.retrieve_feed,
|
|
133
139
|
self.retrieve_ssi,
|
|
@@ -60,6 +60,7 @@ class Operation(ABC, Generic[TParams, TResult]):
|
|
|
60
60
|
workflow_id=workflow_id,
|
|
61
61
|
workflow_status=workflow_result.workflow_status,
|
|
62
62
|
message=workflow_result.message,
|
|
63
|
+
pending_reason=workflow_result.pending_reason,
|
|
63
64
|
)
|
|
64
65
|
|
|
65
66
|
completion = self._get_completion(workflow_result)
|
|
@@ -20,6 +20,8 @@ LinkedApiActionErrorType = Literal[
|
|
|
20
20
|
"postNotFound",
|
|
21
21
|
"jobNotFound",
|
|
22
22
|
"commentingNotAllowed",
|
|
23
|
+
"commentNotFound",
|
|
24
|
+
"replyingNotAllowed",
|
|
23
25
|
"noPostingPermission",
|
|
24
26
|
"noSalesNavigator",
|
|
25
27
|
"conversationsNotSynced",
|
|
@@ -43,6 +45,8 @@ LINKED_API_ACTION_ERROR_TYPES: tuple[str, ...] = (
|
|
|
43
45
|
"postNotFound",
|
|
44
46
|
"jobNotFound",
|
|
45
47
|
"commentingNotAllowed",
|
|
48
|
+
"commentNotFound",
|
|
49
|
+
"replyingNotAllowed",
|
|
46
50
|
"noPostingPermission",
|
|
47
51
|
"noSalesNavigator",
|
|
48
52
|
"conversationsNotSynced",
|
|
@@ -62,6 +66,8 @@ LinkedApiErrorType = Literal[
|
|
|
62
66
|
"linkedinAccountSignedOut",
|
|
63
67
|
"languageNotSupported",
|
|
64
68
|
"workflowTimeout",
|
|
69
|
+
"outsideWorkingHours",
|
|
70
|
+
"workingHoursWaitExpired",
|
|
65
71
|
"httpError",
|
|
66
72
|
"tooManyRequests",
|
|
67
73
|
"accountNotFound",
|
|
@@ -83,6 +89,8 @@ LINKED_API_ERROR_TYPES: tuple[str, ...] = (
|
|
|
83
89
|
"linkedinAccountSignedOut",
|
|
84
90
|
"languageNotSupported",
|
|
85
91
|
"workflowTimeout",
|
|
92
|
+
"outsideWorkingHours",
|
|
93
|
+
"workingHoursWaitExpired",
|
|
86
94
|
"httpError",
|
|
87
95
|
"tooManyRequests",
|
|
88
96
|
"accountNotFound",
|
|
@@ -17,8 +17,10 @@ from linkedapi.operations.nv_search_people import NvSearchPeople
|
|
|
17
17
|
from linkedapi.operations.nv_send_message import NvSendMessage
|
|
18
18
|
from linkedapi.operations.nv_sync_conversation import NvSyncConversation
|
|
19
19
|
from linkedapi.operations.nv_sync_inbox import NvSyncInbox
|
|
20
|
+
from linkedapi.operations.react_to_comment import ReactToComment
|
|
20
21
|
from linkedapi.operations.react_to_post import ReactToPost
|
|
21
22
|
from linkedapi.operations.remove_connection import RemoveConnection
|
|
23
|
+
from linkedapi.operations.reply_to_comment import ReplyToComment
|
|
22
24
|
from linkedapi.operations.retrieve_connections import RetrieveConnections
|
|
23
25
|
from linkedapi.operations.retrieve_feed import RetrieveFeed
|
|
24
26
|
from linkedapi.operations.retrieve_invitations import RetrieveInvitations
|
|
@@ -61,8 +63,10 @@ __all__ = [
|
|
|
61
63
|
"NvSendMessage",
|
|
62
64
|
"NvSyncConversation",
|
|
63
65
|
"NvSyncInbox",
|
|
66
|
+
"ReactToComment",
|
|
64
67
|
"ReactToPost",
|
|
65
68
|
"RemoveConnection",
|
|
69
|
+
"ReplyToComment",
|
|
66
70
|
"RetrieveConnections",
|
|
67
71
|
"RetrieveFeed",
|
|
68
72
|
"RetrieveInvitations",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from linkedapi.core import Operation
|
|
4
|
+
from linkedapi.mappers import SimpleWorkflowMapper
|
|
5
|
+
from linkedapi.types import CommentOnPostParams, CommentResult
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CommentOnPost(Operation[CommentOnPostParams, CommentResult]):
|
|
9
|
+
"""Comment on a LinkedIn post."""
|
|
10
|
+
|
|
11
|
+
operation_name = "commentOnPost"
|
|
12
|
+
mapper = SimpleWorkflowMapper[CommentOnPostParams, CommentResult](
|
|
13
|
+
"st.commentOnPost",
|
|
14
|
+
result_model=CommentResult,
|
|
15
|
+
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from linkedapi.core import Operation
|
|
4
|
+
from linkedapi.mappers import VoidWorkflowMapper
|
|
5
|
+
from linkedapi.types import ReactToCommentParams
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ReactToComment(Operation[ReactToCommentParams, None]):
|
|
9
|
+
"""React to a LinkedIn comment."""
|
|
10
|
+
|
|
11
|
+
operation_name = "reactToComment"
|
|
12
|
+
mapper = VoidWorkflowMapper[ReactToCommentParams]("st.reactToComment")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from linkedapi.core import Operation
|
|
4
|
+
from linkedapi.mappers import SimpleWorkflowMapper
|
|
5
|
+
from linkedapi.types import CommentResult, ReplyToCommentParams
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ReplyToComment(Operation[ReplyToCommentParams, CommentResult]):
|
|
9
|
+
"""Reply to a LinkedIn comment."""
|
|
10
|
+
|
|
11
|
+
operation_name = "replyToComment"
|
|
12
|
+
mapper = SimpleWorkflowMapper[ReplyToCommentParams, CommentResult](
|
|
13
|
+
"st.replyToComment",
|
|
14
|
+
result_model=CommentResult,
|
|
15
|
+
)
|
|
@@ -149,6 +149,8 @@ from linkedapi.types.post import (
|
|
|
149
149
|
BaseFetchPostParamsWide,
|
|
150
150
|
Comment,
|
|
151
151
|
CommentOnPostParams,
|
|
152
|
+
CommentOnPostResult,
|
|
153
|
+
CommentResult,
|
|
152
154
|
CreatePostAttachment,
|
|
153
155
|
CreatePostParams,
|
|
154
156
|
CreatePostResult,
|
|
@@ -168,7 +170,10 @@ from linkedapi.types.post import (
|
|
|
168
170
|
PostType,
|
|
169
171
|
Reaction,
|
|
170
172
|
ReactionType,
|
|
173
|
+
ReactToCommentParams,
|
|
171
174
|
ReactToPostParams,
|
|
175
|
+
ReplyToCommentParams,
|
|
176
|
+
ReplyToCommentResult,
|
|
172
177
|
)
|
|
173
178
|
from linkedapi.types.responses import LinkedApiRequestError, LinkedApiResponse
|
|
174
179
|
from linkedapi.types.search_companies import (
|
|
@@ -230,6 +235,7 @@ from linkedapi.types.workflow import (
|
|
|
230
235
|
WorkflowFailure,
|
|
231
236
|
WorkflowInProgressResponse,
|
|
232
237
|
WorkflowInProgressStatus,
|
|
238
|
+
WorkflowPendingReason,
|
|
233
239
|
WorkflowResponse,
|
|
234
240
|
WorkflowStartedResponse,
|
|
235
241
|
WorkflowStatus,
|
|
@@ -262,6 +268,8 @@ __all__ = [
|
|
|
262
268
|
"CheckConnectionStatusResult",
|
|
263
269
|
"Comment",
|
|
264
270
|
"CommentOnPostParams",
|
|
271
|
+
"CommentOnPostResult",
|
|
272
|
+
"CommentResult",
|
|
265
273
|
"Company",
|
|
266
274
|
"ConnectionPerson",
|
|
267
275
|
"ConnectionSession",
|
|
@@ -372,6 +380,7 @@ __all__ = [
|
|
|
372
380
|
"PostReactionsRetrievalConfig",
|
|
373
381
|
"PostReposter",
|
|
374
382
|
"PostType",
|
|
383
|
+
"ReactToCommentParams",
|
|
375
384
|
"ReactToPostParams",
|
|
376
385
|
"Reaction",
|
|
377
386
|
"ReactionType",
|
|
@@ -381,6 +390,8 @@ __all__ = [
|
|
|
381
390
|
"ReparseAccountInfoParams",
|
|
382
391
|
"ReparseAccountInfoResult",
|
|
383
392
|
"ReplayWebhookDeliveryParams",
|
|
393
|
+
"ReplyToCommentParams",
|
|
394
|
+
"ReplyToCommentResult",
|
|
384
395
|
"ResetLimitsParams",
|
|
385
396
|
"RetrieveConnectionsFilter",
|
|
386
397
|
"RetrieveConnectionsParams",
|
|
@@ -438,6 +449,7 @@ __all__ = [
|
|
|
438
449
|
"WorkflowFailure",
|
|
439
450
|
"WorkflowInProgressResponse",
|
|
440
451
|
"WorkflowInProgressStatus",
|
|
452
|
+
"WorkflowPendingReason",
|
|
441
453
|
"WorkflowResponse",
|
|
442
454
|
"WorkflowStartedResponse",
|
|
443
455
|
"WorkflowStatus",
|
|
@@ -40,7 +40,30 @@ class CommentOnPostParams(BaseActionParams):
|
|
|
40
40
|
company_url: str | None = None
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
class CommentResult(LinkedApiModel):
|
|
44
|
+
comment_urn: str | None = None
|
|
45
|
+
comment_url: str | None = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
CommentOnPostResult: TypeAlias = CommentResult
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ReactToCommentParams(BaseActionParams):
|
|
52
|
+
comment_url: str
|
|
53
|
+
type: ReactionType = "like"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ReplyToCommentParams(BaseActionParams):
|
|
57
|
+
comment_url: str
|
|
58
|
+
text: str
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
ReplyToCommentResult: TypeAlias = CommentResult
|
|
62
|
+
|
|
63
|
+
|
|
43
64
|
class PostComment(LinkedApiModel):
|
|
65
|
+
comment_urn: str | None = None
|
|
66
|
+
comment_url: str | None = None
|
|
44
67
|
commenter_url: str | None = None
|
|
45
68
|
commenter_name: str | None = None
|
|
46
69
|
commenter_headline: str | None = None
|
|
@@ -9,6 +9,7 @@ WorkflowPendingStatus = Literal["pending"]
|
|
|
9
9
|
WorkflowRunningStatus = Literal["running"]
|
|
10
10
|
WorkflowInProgressStatus = Literal["pending", "running"]
|
|
11
11
|
WorkflowStatus = Literal["pending", "running", "completed", "failed"]
|
|
12
|
+
WorkflowPendingReason = Literal["queued", "outsideWorkingHours"]
|
|
12
13
|
WorkflowDefinition = dict[str, Any] | list[dict[str, Any]]
|
|
13
14
|
WorkflowCompletion = dict[str, Any] | list[dict[str, Any]]
|
|
14
15
|
|
|
@@ -22,12 +23,15 @@ class WorkflowStartedResponse(LinkedApiModel):
|
|
|
22
23
|
workflow_id: str
|
|
23
24
|
workflow_status: WorkflowInProgressStatus
|
|
24
25
|
message: str | None = None
|
|
26
|
+
# Optional so an older API that does not send it still parses.
|
|
27
|
+
pending_reason: WorkflowPendingReason | None = None
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
class WorkflowInProgressResponse(LinkedApiModel):
|
|
28
31
|
workflow_id: str
|
|
29
32
|
workflow_status: WorkflowInProgressStatus
|
|
30
33
|
message: str | None = None
|
|
34
|
+
pending_reason: WorkflowPendingReason | None = None
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
class WorkflowFailure(LinkedApiModel):
|
|
@@ -43,6 +47,7 @@ class WorkflowResponse(LinkedApiModel):
|
|
|
43
47
|
workflow_id: str
|
|
44
48
|
workflow_status: WorkflowStatus
|
|
45
49
|
message: str | None = None
|
|
50
|
+
pending_reason: WorkflowPendingReason | None = None
|
|
46
51
|
completion: WorkflowCompletion | None = None
|
|
47
52
|
failure: WorkflowFailure | None = None
|
|
48
53
|
|
|
@@ -21,6 +21,8 @@ def test_error_type_sets_match_node_contract() -> None:
|
|
|
21
21
|
"linkedinAccountSignedOut",
|
|
22
22
|
"languageNotSupported",
|
|
23
23
|
"workflowTimeout",
|
|
24
|
+
"outsideWorkingHours",
|
|
25
|
+
"workingHoursWaitExpired",
|
|
24
26
|
"httpError",
|
|
25
27
|
"tooManyRequests",
|
|
26
28
|
"accountNotFound",
|
|
@@ -47,6 +49,8 @@ def test_error_type_sets_match_node_contract() -> None:
|
|
|
47
49
|
"postNotFound",
|
|
48
50
|
"jobNotFound",
|
|
49
51
|
"commentingNotAllowed",
|
|
52
|
+
"commentNotFound",
|
|
53
|
+
"replyingNotAllowed",
|
|
50
54
|
"noPostingPermission",
|
|
51
55
|
"noSalesNavigator",
|
|
52
56
|
"conversationsNotSynced",
|
|
@@ -13,6 +13,7 @@ from linkedapi import (
|
|
|
13
13
|
LinkedApi,
|
|
14
14
|
LinkedApiConfig,
|
|
15
15
|
RetrieveFeedParams,
|
|
16
|
+
SimpleWorkflowMapper,
|
|
16
17
|
VoidWorkflowMapper,
|
|
17
18
|
)
|
|
18
19
|
|
|
@@ -53,6 +54,8 @@ def test_linked_api_exposes_all_predefined_operations() -> None:
|
|
|
53
54
|
"nv_manage_conversation",
|
|
54
55
|
"react_to_post",
|
|
55
56
|
"comment_on_post",
|
|
57
|
+
"react_to_comment",
|
|
58
|
+
"reply_to_comment",
|
|
56
59
|
"create_post",
|
|
57
60
|
"retrieve_ssi",
|
|
58
61
|
"retrieve_performance",
|
|
@@ -63,7 +66,7 @@ def test_linked_api_exposes_all_predefined_operations() -> None:
|
|
|
63
66
|
assert hasattr(operation, "execute")
|
|
64
67
|
assert hasattr(operation, "result")
|
|
65
68
|
assert hasattr(operation, "cancel")
|
|
66
|
-
assert len(linkedapi.operations) ==
|
|
69
|
+
assert len(linkedapi.operations) == 38
|
|
67
70
|
|
|
68
71
|
|
|
69
72
|
def test_operation_mappers_match_node_contract() -> None:
|
|
@@ -95,6 +98,12 @@ def test_operation_mappers_match_node_contract() -> None:
|
|
|
95
98
|
assert linkedapi.manage_conversation.mapper.action_type == "st.manageConversation"
|
|
96
99
|
assert isinstance(linkedapi.nv_manage_conversation.mapper, VoidWorkflowMapper)
|
|
97
100
|
assert linkedapi.nv_manage_conversation.mapper.action_type == "nv.manageConversation"
|
|
101
|
+
assert isinstance(linkedapi.react_to_comment.mapper, VoidWorkflowMapper)
|
|
102
|
+
assert linkedapi.react_to_comment.mapper.action_type == "st.reactToComment"
|
|
103
|
+
assert isinstance(linkedapi.comment_on_post.mapper, SimpleWorkflowMapper)
|
|
104
|
+
assert linkedapi.comment_on_post.mapper.action_type == "st.commentOnPost"
|
|
105
|
+
assert isinstance(linkedapi.reply_to_comment.mapper, SimpleWorkflowMapper)
|
|
106
|
+
assert linkedapi.reply_to_comment.mapper.action_type == "st.replyToComment"
|
|
98
107
|
|
|
99
108
|
|
|
100
109
|
def test_retrieve_feed_maps_params_and_feed_context() -> None:
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
from unittest.mock import Mock
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from linkedapi.core.operation import Operation
|
|
9
|
+
from linkedapi.errors import LINKED_API_ERROR_TYPES
|
|
10
|
+
from linkedapi.types import (
|
|
11
|
+
WorkflowInProgressResponse,
|
|
12
|
+
WorkflowResponse,
|
|
13
|
+
WorkflowStartedResponse,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class _StubMapper:
|
|
18
|
+
def map_request(self, params: Any) -> Any:
|
|
19
|
+
return params
|
|
20
|
+
|
|
21
|
+
def map_response(self, completion: Any) -> Any:
|
|
22
|
+
return completion
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _StubOperation(Operation): # type: ignore[type-arg]
|
|
26
|
+
operation_name = "fetchPerson"
|
|
27
|
+
|
|
28
|
+
def __init__(self, http_client: Any) -> None:
|
|
29
|
+
super().__init__(http_client)
|
|
30
|
+
self.mapper = _StubMapper() # type: ignore[assignment]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _operation(workflow_result: dict[str, Any]) -> _StubOperation:
|
|
34
|
+
http_client = Mock()
|
|
35
|
+
http_client.get.return_value = Mock(
|
|
36
|
+
error=None,
|
|
37
|
+
result=WorkflowResponse.model_validate(workflow_result),
|
|
38
|
+
)
|
|
39
|
+
return _StubOperation(http_client)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestPendingReasonModels:
|
|
43
|
+
def test_in_progress_response_accepts_a_pending_reason(self) -> None:
|
|
44
|
+
response = WorkflowInProgressResponse(
|
|
45
|
+
workflowId="wf-1",
|
|
46
|
+
workflowStatus="pending",
|
|
47
|
+
pendingReason="outsideWorkingHours",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
assert response.pending_reason == "outsideWorkingHours"
|
|
51
|
+
|
|
52
|
+
def test_started_response_accepts_a_pending_reason(self) -> None:
|
|
53
|
+
response = WorkflowStartedResponse(
|
|
54
|
+
workflowId="wf-1",
|
|
55
|
+
workflowStatus="pending",
|
|
56
|
+
pendingReason="queued",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
assert response.pending_reason == "queued"
|
|
60
|
+
|
|
61
|
+
@pytest.mark.parametrize(
|
|
62
|
+
"model",
|
|
63
|
+
[WorkflowInProgressResponse, WorkflowStartedResponse, WorkflowResponse],
|
|
64
|
+
)
|
|
65
|
+
def test_field_is_optional_for_back_compat(self, model: Any) -> None:
|
|
66
|
+
response = model(workflowId="wf-1", workflowStatus="running")
|
|
67
|
+
|
|
68
|
+
assert response.pending_reason is None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class TestStatusCarriesPendingReason:
|
|
72
|
+
def test_carries_the_outside_working_hours_reason(self) -> None:
|
|
73
|
+
operation = _operation(
|
|
74
|
+
{
|
|
75
|
+
"workflowId": "wf-1",
|
|
76
|
+
"workflowStatus": "pending",
|
|
77
|
+
"pendingReason": "outsideWorkingHours",
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
result = operation.status("wf-1")
|
|
82
|
+
|
|
83
|
+
assert isinstance(result, WorkflowInProgressResponse)
|
|
84
|
+
assert result.pending_reason == "outsideWorkingHours"
|
|
85
|
+
|
|
86
|
+
def test_carries_the_queued_reason(self) -> None:
|
|
87
|
+
operation = _operation(
|
|
88
|
+
{"workflowId": "wf-1", "workflowStatus": "pending", "pendingReason": "queued"}
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
result = operation.status("wf-1")
|
|
92
|
+
|
|
93
|
+
assert isinstance(result, WorkflowInProgressResponse)
|
|
94
|
+
assert result.pending_reason == "queued"
|
|
95
|
+
|
|
96
|
+
def test_keeps_none_when_the_api_sends_nothing(self) -> None:
|
|
97
|
+
operation = _operation({"workflowId": "wf-1", "workflowStatus": "running"})
|
|
98
|
+
|
|
99
|
+
result = operation.status("wf-1")
|
|
100
|
+
|
|
101
|
+
assert isinstance(result, WorkflowInProgressResponse)
|
|
102
|
+
assert result.pending_reason is None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class TestWorkingHoursErrorTypes:
|
|
106
|
+
@pytest.mark.parametrize("error_type", ["outsideWorkingHours", "workingHoursWaitExpired"])
|
|
107
|
+
def test_error_type_is_known(self, error_type: str) -> None:
|
|
108
|
+
assert error_type in LINKED_API_ERROR_TYPES
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from linkedapi.core import Operation
|
|
4
|
-
from linkedapi.mappers import VoidWorkflowMapper
|
|
5
|
-
from linkedapi.types import CommentOnPostParams
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class CommentOnPost(Operation[CommentOnPostParams, None]):
|
|
9
|
-
"""Comment on a LinkedIn post."""
|
|
10
|
-
|
|
11
|
-
operation_name = "commentOnPost"
|
|
12
|
-
mapper = VoidWorkflowMapper[CommentOnPostParams]("st.commentOnPost")
|
|
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
|