Skip to content

Commit d01ccfb

Browse files
Merge pull request #2 from Linked-API/task/endorse-sdk
Add skill endorsement operations; accept future API values
1 parent 2683154 commit d01ccfb

31 files changed

Lines changed: 1013 additions & 78 deletions

‎linkedapi/__init__.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
CreatePost,
3737
CreateRepost,
3838
CustomWorkflow,
39+
EndorsePersonSkill,
40+
EndorsePersonSkills,
3941
FetchCompany,
4042
FetchCompanyMapper,
4143
FetchJob,
@@ -87,7 +89,7 @@
8789
verify_webhook_signature,
8890
)
8991

90-
__version__ = "1.4.6"
92+
__version__ = "1.4.7"
9193
PredefinedOperation = Operation
9294

9395
__all__ = [
@@ -106,6 +108,8 @@
106108
"CreatePost",
107109
"CreateRepost",
108110
"CustomWorkflow",
111+
"EndorsePersonSkill",
112+
"EndorsePersonSkills",
109113
"FetchCompany",
110114
"FetchCompanyMapper",
111115
"FetchJob",

‎linkedapi/client.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
CreatePost,
1515
CreateRepost,
1616
CustomWorkflow,
17+
EndorsePersonSkill,
18+
EndorsePersonSkills,
1719
FetchCompany,
1820
FetchJob,
1921
FetchPerson,
@@ -94,6 +96,8 @@ def __init__(self, config: LinkedApiConfig | HttpClient[Any]) -> None:
9496
self.search_posts = SearchPosts(self.http_client)
9597
self.fetch_company = FetchCompany(self.http_client)
9698
self.fetch_person = FetchPerson(self.http_client)
99+
self.endorse_person_skill = EndorsePersonSkill(self.http_client)
100+
self.endorse_person_skills = EndorsePersonSkills(self.http_client)
97101
self.fetch_post = FetchPost(self.http_client)
98102
self.fetch_job = FetchJob(self.http_client)
99103
self.react_to_post = ReactToPost(self.http_client)
@@ -137,6 +141,8 @@ def __init__(self, config: LinkedApiConfig | HttpClient[Any]) -> None:
137141
self.search_posts,
138142
self.fetch_company,
139143
self.fetch_person,
144+
self.endorse_person_skill,
145+
self.endorse_person_skills,
140146
self.fetch_post,
141147
self.fetch_job,
142148
self.react_to_post,

‎linkedapi/errors.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
"notPending",
1616
"retrievingNotAllowed",
1717
"connectionNotFound",
18+
"skillNotFound",
19+
"endorsementUnavailable",
20+
"limitExceeded",
1821
"searchingNotAllowed",
1922
"searchInterfaceMismatch",
2023
"filterNotApplied",
@@ -46,6 +49,9 @@
4649
"notPending",
4750
"retrievingNotAllowed",
4851
"connectionNotFound",
52+
"skillNotFound",
53+
"endorsementUnavailable",
54+
"limitExceeded",
4955
"searchingNotAllowed",
5056
"searchInterfaceMismatch",
5157
"filterNotApplied",

‎linkedapi/mappers/__init__.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from linkedapi.mappers.array import ArrayWorkflowMapper
22
from linkedapi.mappers.base import BaseMapper, MappedResponse
3+
from linkedapi.mappers.endorse_person_skills import EndorsePersonSkillsMapper
34
from linkedapi.mappers.send_message import SendMessageMapper
45
from linkedapi.mappers.simple import SimpleWorkflowMapper
56
from linkedapi.mappers.then import ActionConfig, ResponseMapping, ThenWorkflowMapper
@@ -9,6 +10,7 @@
910
"ActionConfig",
1011
"ArrayWorkflowMapper",
1112
"BaseMapper",
13+
"EndorsePersonSkillsMapper",
1214
"MappedResponse",
1315
"ResponseMapping",
1416
"SendMessageMapper",
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from __future__ import annotations
2+
3+
from typing import Any, cast
4+
5+
from linkedapi.errors import LinkedApiError
6+
from linkedapi.mappers.base import (
7+
BaseMapper,
8+
MappedResponse,
9+
as_action_dict,
10+
parse_action_error,
11+
parse_result,
12+
)
13+
from linkedapi.types import (
14+
EndorsePersonSkillsParams,
15+
PersonSkillEndorsement,
16+
WorkflowCompletion,
17+
WorkflowDefinition,
18+
serialize_model,
19+
)
20+
21+
22+
class EndorsePersonSkillsMapper(
23+
BaseMapper[EndorsePersonSkillsParams, list[PersonSkillEndorsement]]
24+
):
25+
def map_request(self, params: EndorsePersonSkillsParams | None = None) -> WorkflowDefinition:
26+
serialized = serialize_model(params)
27+
skill_name_patterns = serialized.pop("skillNamePatterns", None)
28+
do_for_skills: dict[str, Any] = {
29+
"actionType": "st.doForSkills",
30+
"then": {"actionType": "st.endorsePersonSkill"},
31+
}
32+
if skill_name_patterns is not None:
33+
do_for_skills["skillNamePatterns"] = skill_name_patterns
34+
35+
return {
36+
"actionType": "st.openPersonPage",
37+
"basicInfo": False,
38+
**serialized,
39+
"then": {"actionType": "st.retrievePersonSkills", "then": do_for_skills},
40+
}
41+
42+
def map_response(
43+
self, completion: WorkflowCompletion
44+
) -> MappedResponse[list[PersonSkillEndorsement]]:
45+
action = as_action_dict(completion)
46+
error = parse_action_error(action.get("error"))
47+
if error is not None:
48+
return MappedResponse(data=None, errors=[error])
49+
50+
data = action.get("data")
51+
if data is None:
52+
raise LinkedApiError.unknown_error()
53+
then_actions = as_action_dict(data).get("then")
54+
child_actions = then_actions if isinstance(then_actions, list) else [then_actions]
55+
for child in child_actions:
56+
skills_action = as_action_dict(child)
57+
if skills_action.get("actionType") == "st.retrievePersonSkills":
58+
return self._map_skills(skills_action)
59+
raise LinkedApiError.unknown_error()
60+
61+
def _map_skills(self, action: dict[str, Any]) -> MappedResponse[list[PersonSkillEndorsement]]:
62+
error = parse_action_error(action.get("error"))
63+
if error is not None:
64+
return MappedResponse(data=None, errors=[error])
65+
66+
selected_skills: list[dict[str, Any]] = []
67+
for skill in action["data"]:
68+
for child in skill.get("then", []):
69+
endorsement = as_action_dict(child)
70+
if endorsement.get("actionType") == "st.endorsePersonSkill":
71+
selected_skills.append(
72+
{
73+
"skillName": skill["name"],
74+
"success": endorsement["success"],
75+
"error": endorsement.get("error"),
76+
}
77+
)
78+
break
79+
return MappedResponse(
80+
data=cast(
81+
list[PersonSkillEndorsement], parse_result(selected_skills, PersonSkillEndorsement)
82+
),
83+
errors=[],
84+
)

‎linkedapi/operations/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from linkedapi.operations.create_post import CreatePost
55
from linkedapi.operations.create_repost import CreateRepost
66
from linkedapi.operations.custom_workflow import CustomWorkflow
7+
from linkedapi.operations.endorse_person_skill import EndorsePersonSkill
8+
from linkedapi.operations.endorse_person_skills import EndorsePersonSkills
79
from linkedapi.operations.fetch_company import FetchCompany, FetchCompanyMapper
810
from linkedapi.operations.fetch_job import FetchJob, FetchJobMapper
911
from linkedapi.operations.fetch_person import FetchPerson, FetchPersonMapper
@@ -47,6 +49,8 @@
4749
"CreatePost",
4850
"CreateRepost",
4951
"CustomWorkflow",
52+
"EndorsePersonSkill",
53+
"EndorsePersonSkills",
5054
"FetchCompany",
5155
"FetchCompanyMapper",
5256
"FetchJob",
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 EndorsePersonSkillParams
6+
7+
8+
class EndorsePersonSkill(Operation[EndorsePersonSkillParams, None]):
9+
"""Endorse a first-degree connection's skill by its exact name."""
10+
11+
operation_name = "endorsePersonSkill"
12+
mapper = VoidWorkflowMapper[EndorsePersonSkillParams]("st.endorsePersonSkill")
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from __future__ import annotations
2+
3+
from linkedapi.core import Operation
4+
from linkedapi.mappers import EndorsePersonSkillsMapper
5+
from linkedapi.types import EndorsePersonSkillsParams, PersonSkillEndorsement
6+
7+
8+
class EndorsePersonSkills(Operation[EndorsePersonSkillsParams, list[PersonSkillEndorsement]]):
9+
"""Endorse selected skills and return each skill's outcome in profile order."""
10+
11+
operation_name = "endorsePersonSkills"
12+
mapper = EndorsePersonSkillsMapper()

‎linkedapi/types/__init__.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@
142142
BaseFetchPersonParams,
143143
BaseFetchPersonParamsWide,
144144
EmploymentType,
145+
EndorsePersonSkillParams,
146+
EndorsePersonSkillsParams,
145147
FetchPersonParams,
146148
FetchPersonResult,
147149
LanguageProficiency,
@@ -151,6 +153,7 @@
151153
PersonExperience,
152154
PersonLanguage,
153155
PersonSkill,
156+
PersonSkillEndorsement,
154157
YearsOfExperience,
155158
)
156159
from linkedapi.types.post import (
@@ -262,6 +265,7 @@
262265
SetWebhookParams,
263266
SetWebhookPayloadModeParams,
264267
SetWebhookSigningParams,
268+
UnknownWebhookEvent,
265269
WebhookDelivery,
266270
WebhookDeliveryStatus,
267271
WebhookEvent,
@@ -347,6 +351,8 @@
347351
"DeleteWebhookParams",
348352
"DisconnectParams",
349353
"EmploymentType",
354+
"EndorsePersonSkillParams",
355+
"EndorsePersonSkillsParams",
350356
"FeedPost",
351357
"FetchCompanyParams",
352358
"FetchCompanyResult",
@@ -433,6 +439,7 @@
433439
"PersonExperience",
434440
"PersonLanguage",
435441
"PersonSkill",
442+
"PersonSkillEndorsement",
436443
"PersonViewerState",
437444
"Post",
438445
"PostActorType",
@@ -526,6 +533,7 @@
526533
"SyncConversationResult",
527534
"SyncInboxParams",
528535
"SyncNetworkParams",
536+
"UnknownWebhookEvent",
529537
"WebhookDelivery",
530538
"WebhookDeliveryStatus",
531539
"WebhookEvent",

‎linkedapi/types/admin/accounts.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class AdminAccount(LinkedApiModel):
2525
headline: str | None = None
2626
country_code: str | None = None
2727
identification_token: str | None = None
28-
status: AdminAccountStatus | None = None
28+
status: AdminAccountStatus | str | None = None
2929
connected_at: str | None = None
3030
reconnection_session_id: str | None = None
3131
reconnection_link: str | None = None
@@ -81,7 +81,7 @@ class GetConnectionSessionParams(LinkedApiModel):
8181

8282
class ConnectionSession(LinkedApiModel):
8383
session_id: str | None = None
84-
status: ConnectionSessionStatus | None = None
84+
status: ConnectionSessionStatus | str | None = None
8585
type: str | None = None
8686

8787

0 commit comments

Comments
 (0)