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.
Files changed (107) hide show
  1. {linkedapi-1.3.4 → linkedapi-1.3.6}/PKG-INFO +1 -1
  2. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/__init__.py +5 -1
  3. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/client.py +6 -0
  4. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/core/operation.py +1 -0
  5. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/errors.py +8 -0
  6. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/__init__.py +4 -0
  7. linkedapi-1.3.6/linkedapi/operations/comment_on_post.py +15 -0
  8. linkedapi-1.3.6/linkedapi/operations/react_to_comment.py +12 -0
  9. linkedapi-1.3.6/linkedapi/operations/reply_to_comment.py +15 -0
  10. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/__init__.py +12 -0
  11. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/post.py +23 -0
  12. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/workflow.py +5 -0
  13. {linkedapi-1.3.4 → linkedapi-1.3.6}/pyproject.toml +1 -1
  14. {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_errors.py +4 -0
  15. {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_operations.py +10 -1
  16. linkedapi-1.3.6/tests/test_working_hours_contract.py +108 -0
  17. linkedapi-1.3.4/linkedapi/operations/comment_on_post.py +0 -12
  18. {linkedapi-1.3.4 → linkedapi-1.3.6}/.github/workflows/publish.yaml +0 -0
  19. {linkedapi-1.3.4 → linkedapi-1.3.6}/.gitignore +0 -0
  20. {linkedapi-1.3.4 → linkedapi-1.3.6}/LICENSE +0 -0
  21. {linkedapi-1.3.4 → linkedapi-1.3.6}/README.md +0 -0
  22. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/__init__.py +0 -0
  23. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/accounts.py +0 -0
  24. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/admin.py +0 -0
  25. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/http_client.py +0 -0
  26. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/limits.py +0 -0
  27. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/subscription.py +0 -0
  28. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/admin/webhooks.py +0 -0
  29. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/config.py +0 -0
  30. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/core/__init__.py +0 -0
  31. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/core/polling.py +0 -0
  32. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/http/__init__.py +0 -0
  33. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/http/base.py +0 -0
  34. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/http/linked_api_http_client.py +0 -0
  35. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/__init__.py +0 -0
  36. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/array.py +0 -0
  37. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/base.py +0 -0
  38. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/send_message.py +0 -0
  39. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/simple.py +0 -0
  40. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/then.py +0 -0
  41. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/mappers/void.py +0 -0
  42. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/accept_invitation.py +0 -0
  43. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/check_connection_status.py +0 -0
  44. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/create_post.py +0 -0
  45. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/custom_workflow.py +0 -0
  46. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/fetch_company.py +0 -0
  47. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/fetch_job.py +0 -0
  48. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/fetch_person.py +0 -0
  49. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/fetch_post.py +0 -0
  50. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/ignore_invitation.py +0 -0
  51. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/manage_conversation.py +0 -0
  52. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_fetch_company.py +0 -0
  53. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_fetch_person.py +0 -0
  54. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_manage_conversation.py +0 -0
  55. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_search_companies.py +0 -0
  56. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_search_people.py +0 -0
  57. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_send_message.py +0 -0
  58. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_sync_conversation.py +0 -0
  59. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/nv_sync_inbox.py +0 -0
  60. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/react_to_post.py +0 -0
  61. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/remove_connection.py +0 -0
  62. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_connections.py +0 -0
  63. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_feed.py +0 -0
  64. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_invitations.py +0 -0
  65. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_pending_requests.py +0 -0
  66. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_performance.py +0 -0
  67. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/retrieve_ssi.py +0 -0
  68. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/search_companies.py +0 -0
  69. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/search_jobs.py +0 -0
  70. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/search_people.py +0 -0
  71. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/send_connection_request.py +0 -0
  72. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/send_message.py +0 -0
  73. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/sync_conversation.py +0 -0
  74. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/sync_inbox.py +0 -0
  75. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/sync_network.py +0 -0
  76. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/operations/withdraw_connection_request.py +0 -0
  77. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/py.typed +0 -0
  78. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/account.py +0 -0
  79. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/admin/__init__.py +0 -0
  80. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/admin/accounts.py +0 -0
  81. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/admin/config.py +0 -0
  82. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/admin/limits.py +0 -0
  83. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/admin/subscription.py +0 -0
  84. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/base.py +0 -0
  85. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/company.py +0 -0
  86. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/connection.py +0 -0
  87. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/feed.py +0 -0
  88. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/jobs.py +0 -0
  89. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/message.py +0 -0
  90. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/network.py +0 -0
  91. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/params.py +0 -0
  92. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/person.py +0 -0
  93. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/responses.py +0 -0
  94. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/search_companies.py +0 -0
  95. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/search_people.py +0 -0
  96. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/statistics.py +0 -0
  97. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/types/webhooks.py +0 -0
  98. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/webhooks/__init__.py +0 -0
  99. {linkedapi-1.3.4 → linkedapi-1.3.6}/linkedapi/webhooks/parse.py +0 -0
  100. {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/conftest.py +0 -0
  101. {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_admin.py +0 -0
  102. {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_direct_methods.py +0 -0
  103. {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_http_client.py +0 -0
  104. {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_mappers.py +0 -0
  105. {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_models.py +0 -0
  106. {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_polling.py +0 -0
  107. {linkedapi-1.3.4 → linkedapi-1.3.6}/tests/test_webhooks.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: linkedapi
3
- Version: 1.3.4
3
+ Version: 1.3.6
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
@@ -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.4"
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
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "linkedapi"
7
- version = "1.3.4"
7
+ version = "1.3.6"
8
8
  description = "Official synchronous Python SDK for Linked API."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -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) == 36
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