@entergreat/unipile-wrapper 1.0.11 → 2.1.0
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.
- package/README.md +290 -161
- package/dist/client/httpClient.d.ts +14 -0
- package/dist/client/httpClient.d.ts.map +1 -0
- package/dist/client/httpClient.js +50 -0
- package/dist/client/httpClient.js.map +1 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/services/accountService.d.ts +11 -0
- package/dist/services/accountService.d.ts.map +1 -0
- package/dist/services/accountService.js +17 -0
- package/dist/services/accountService.js.map +1 -0
- package/dist/services/baseService.d.ts +10 -0
- package/dist/services/baseService.d.ts.map +1 -0
- package/dist/services/baseService.js +23 -0
- package/dist/services/baseService.js.map +1 -0
- package/dist/services/companyService.d.ts +11 -0
- package/dist/services/companyService.d.ts.map +1 -0
- package/dist/services/companyService.js +26 -0
- package/dist/services/companyService.js.map +1 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +6 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/profileService.d.ts +20 -0
- package/dist/services/profileService.d.ts.map +1 -0
- package/dist/services/profileService.js +85 -0
- package/dist/services/profileService.js.map +1 -0
- package/dist/services/searchService.d.ts +19 -0
- package/dist/services/searchService.d.ts.map +1 -0
- package/dist/services/searchService.js +54 -0
- package/dist/services/searchService.js.map +1 -0
- package/dist/types/account.d.ts +8 -0
- package/dist/types/account.d.ts.map +1 -0
- package/dist/types/account.js +2 -0
- package/dist/types/account.js.map +1 -0
- package/dist/types/common.d.ts +19 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +2 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/company.d.ts +11 -0
- package/dist/types/company.d.ts.map +1 -0
- package/dist/types/company.js +2 -0
- package/dist/types/company.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/profile.d.ts +46 -0
- package/dist/types/profile.d.ts.map +1 -0
- package/dist/types/profile.js +2 -0
- package/dist/types/profile.js.map +1 -0
- package/dist/types/search.d.ts +22 -0
- package/dist/types/search.d.ts.map +1 -0
- package/dist/types/search.js +2 -0
- package/dist/types/search.js.map +1 -0
- package/dist/utils/helpers.d.ts +14 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +34 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/urlBuilder.d.ts +11 -0
- package/dist/utils/urlBuilder.d.ts.map +1 -0
- package/dist/utils/urlBuilder.js +23 -0
- package/dist/utils/urlBuilder.js.map +1 -0
- package/dist/utils/validators.d.ts +13 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +39 -0
- package/dist/utils/validators.js.map +1 -0
- package/package.json +19 -8
- package/src/client/httpClient.ts +61 -0
- package/src/client/index.ts +1 -0
- package/src/index.ts +31 -0
- package/src/services/accountService.ts +21 -0
- package/src/services/baseService.ts +31 -0
- package/src/services/companyService.ts +37 -0
- package/src/services/index.ts +5 -0
- package/src/services/profileService.ts +142 -0
- package/src/services/searchService.ts +88 -0
- package/src/types/account.ts +9 -0
- package/src/types/common.ts +21 -0
- package/src/types/company.ts +13 -0
- package/src/types/index.ts +5 -0
- package/src/types/profile.ts +50 -0
- package/src/types/search.ts +27 -0
- package/src/utils/helpers.ts +41 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/urlBuilder.ts +29 -0
- package/src/utils/validators.ts +47 -0
- package/tsconfig.json +21 -0
- package/src/index.js +0 -3
- package/src/services/linkedinService.js +0 -262
- package/src/utils/api.js +0 -28
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { AccountIdParam } from "./common.js";
|
|
2
|
+
|
|
3
|
+
export interface RetrieveProfileRequest extends AccountIdParam {
|
|
4
|
+
profileId: string;
|
|
5
|
+
linkedinSections?: string[];
|
|
6
|
+
notify?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ProfileData {
|
|
10
|
+
headline?: string;
|
|
11
|
+
summary?: string;
|
|
12
|
+
location?: { id: string };
|
|
13
|
+
picture?: Record<string, unknown>;
|
|
14
|
+
experience?: {
|
|
15
|
+
skills?: string[];
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
};
|
|
18
|
+
education?: Record<string, unknown>;
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface EditProfileRequest extends AccountIdParam {
|
|
23
|
+
profileData: ProfileData;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ExperienceData {
|
|
27
|
+
role: string;
|
|
28
|
+
company: string;
|
|
29
|
+
company_id?: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
location?: string;
|
|
32
|
+
start_month?: number;
|
|
33
|
+
start_year?: number;
|
|
34
|
+
end_month?: number;
|
|
35
|
+
end_year?: number;
|
|
36
|
+
skills?: string[];
|
|
37
|
+
notify_network?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface AddExperienceRequest extends AccountIdParam {
|
|
41
|
+
experience: ExperienceData;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ProfileResponse {
|
|
45
|
+
object: string;
|
|
46
|
+
id: string;
|
|
47
|
+
name?: string;
|
|
48
|
+
headline?: string;
|
|
49
|
+
// Add more fields as needed
|
|
50
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AccountIdParam, PaginationParams } from "./common.js";
|
|
2
|
+
|
|
3
|
+
export interface SearchByParamsRequest extends AccountIdParam, PaginationParams {
|
|
4
|
+
params?: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface SearchByUrlRequest extends AccountIdParam, PaginationParams {
|
|
8
|
+
searchUrl: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface RetrieveSearchParametersRequest extends AccountIdParam {
|
|
12
|
+
type: string;
|
|
13
|
+
keywords?: string;
|
|
14
|
+
limit?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SearchResult {
|
|
18
|
+
object: string;
|
|
19
|
+
items: unknown[];
|
|
20
|
+
cursor?: string;
|
|
21
|
+
// Add more fields as needed
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SearchParametersResult {
|
|
25
|
+
object: string;
|
|
26
|
+
items: unknown[];
|
|
27
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts company ID from a LinkedIn company URL
|
|
3
|
+
* Handles URLs like:
|
|
4
|
+
* - https://www.linkedin.com/company/company-name/
|
|
5
|
+
* - https://www.linkedin.com/company/12345678/
|
|
6
|
+
* - https://linkedin.com/company/company-name
|
|
7
|
+
*/
|
|
8
|
+
export function extractCompanyIdFromUrl(url: string): string | null {
|
|
9
|
+
const match = url.match(/linkedin\.com\/company\/([^\/\?]+)/i);
|
|
10
|
+
return match ? match[1] : null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Converts nested objects to bracket notation for API compatibility
|
|
15
|
+
* Example: { experience: { role: "Dev" } } -> { "experience[role]": "Dev" }
|
|
16
|
+
*/
|
|
17
|
+
export function convertToBracketNotation(
|
|
18
|
+
obj: Record<string, unknown>,
|
|
19
|
+
prefix = ""
|
|
20
|
+
): Record<string, unknown> {
|
|
21
|
+
const result: Record<string, unknown> = {};
|
|
22
|
+
|
|
23
|
+
for (const key in obj) {
|
|
24
|
+
if (obj[key] === undefined) continue;
|
|
25
|
+
|
|
26
|
+
const fullKey = prefix ? `${prefix}[${key}]` : key;
|
|
27
|
+
|
|
28
|
+
if (Array.isArray(obj[key])) {
|
|
29
|
+
result[fullKey] = obj[key];
|
|
30
|
+
} else if (typeof obj[key] === "object" && obj[key] !== null) {
|
|
31
|
+
Object.assign(
|
|
32
|
+
result,
|
|
33
|
+
convertToBracketNotation(obj[key] as Record<string, unknown>, fullKey)
|
|
34
|
+
);
|
|
35
|
+
} else {
|
|
36
|
+
result[fullKey] = obj[key];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
type QueryParams = Record<string, string | number | boolean | undefined>;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Builds a URL with query parameters, filtering out undefined values
|
|
5
|
+
*/
|
|
6
|
+
export function buildUrl(baseUrl: string, path: string, params: QueryParams = {}): string {
|
|
7
|
+
const url = new URL(path, baseUrl);
|
|
8
|
+
|
|
9
|
+
for (const [key, value] of Object.entries(params)) {
|
|
10
|
+
if (value !== undefined && value !== "") {
|
|
11
|
+
url.searchParams.append(key, String(value));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return url.toString();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Appends array values as repeated query parameters
|
|
20
|
+
*/
|
|
21
|
+
export function appendArrayParams(url: string, key: string, values: string[]): string {
|
|
22
|
+
const urlObj = new URL(url);
|
|
23
|
+
|
|
24
|
+
for (const value of values) {
|
|
25
|
+
urlObj.searchParams.append(key, value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return urlObj.toString();
|
|
29
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export class ValidationError extends Error {
|
|
2
|
+
constructor(message: string) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "ValidationError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function validateRequired(
|
|
9
|
+
params: Record<string, unknown>,
|
|
10
|
+
requiredFields: string[]
|
|
11
|
+
): void {
|
|
12
|
+
for (const field of requiredFields) {
|
|
13
|
+
if (!params[field]) {
|
|
14
|
+
throw new ValidationError(`${field} must be provided`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function validateAccountId(accountId: string | undefined): void {
|
|
20
|
+
if (!accountId) {
|
|
21
|
+
throw new ValidationError("accountId must be provided");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function validateProfileId(profileId: string | undefined): void {
|
|
26
|
+
if (!profileId) {
|
|
27
|
+
throw new ValidationError("profileId must be provided");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function validateExperience(experience: { role?: string; company?: string } | undefined): void {
|
|
32
|
+
if (!experience || !experience.role || !experience.company) {
|
|
33
|
+
throw new ValidationError("experience with role and company is required");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function validateProfileData(profileData: Record<string, unknown> | undefined): void {
|
|
38
|
+
if (!profileData || Object.keys(profileData).length === 0) {
|
|
39
|
+
throw new ValidationError("profileData must be provided with at least one field to update");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function validateCompanyIdOrUrl(companyId?: string, companyUrl?: string): void {
|
|
44
|
+
if (!companyId && !companyUrl) {
|
|
45
|
+
throw new ValidationError("companyId or companyUrl must be provided");
|
|
46
|
+
}
|
|
47
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationMap": true,
|
|
9
|
+
"sourceMap": true,
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"rootDir": "./src",
|
|
12
|
+
"strict": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true
|
|
18
|
+
},
|
|
19
|
+
"include": ["src/**/*"],
|
|
20
|
+
"exclude": ["node_modules", "dist"]
|
|
21
|
+
}
|
package/src/index.js
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
import { get, post, patch } from "../utils/api.js";
|
|
2
|
-
import FormData from "form-data";
|
|
3
|
-
|
|
4
|
-
function extractCompanyIdFromUrl(url) {
|
|
5
|
-
// Handle URLs like:
|
|
6
|
-
// https://www.linkedin.com/company/company-name/
|
|
7
|
-
// https://www.linkedin.com/company/12345678/
|
|
8
|
-
// https://linkedin.com/company/company-name
|
|
9
|
-
const match = url.match(/linkedin\.com\/company\/([^\/\?]+)/i);
|
|
10
|
-
return match ? match[1] : null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function convertToBracketNotation(obj, prefix = '') {
|
|
14
|
-
const result = {};
|
|
15
|
-
|
|
16
|
-
for (const key in obj) {
|
|
17
|
-
if (obj[key] === undefined) continue;
|
|
18
|
-
|
|
19
|
-
const fullKey = prefix ? `${prefix}[${key}]` : key;
|
|
20
|
-
|
|
21
|
-
if (Array.isArray(obj[key])) {
|
|
22
|
-
result[fullKey] = obj[key];
|
|
23
|
-
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
24
|
-
Object.assign(result, convertToBracketNotation(obj[key], fullKey));
|
|
25
|
-
} else {
|
|
26
|
-
result[fullKey] = obj[key];
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return result;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export default class LinkedinService {
|
|
34
|
-
constructor({ uniphileToken, subdomain, port }) {
|
|
35
|
-
this.unipileUrl = `https://${subdomain}.unipile.com:${port}/api/v1`;
|
|
36
|
-
this.headers = {
|
|
37
|
-
accept: "application/json",
|
|
38
|
-
"content-type": "application/json",
|
|
39
|
-
"X-API-KEY": uniphileToken,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// connect to linkedin account via li_at cookie
|
|
44
|
-
async connect({ loginCookie }) {
|
|
45
|
-
try {
|
|
46
|
-
const body = {
|
|
47
|
-
provider: "LINKEDIN",
|
|
48
|
-
access_token: loginCookie,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
return await post(`${this.unipileUrl}/accounts`, body, this.headers);
|
|
52
|
-
} catch (error) {
|
|
53
|
-
throw error;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// preform a linkedin search by params
|
|
58
|
-
async searchByParams({ accountId, params = {}, cursor = "", limit = 10 }) {
|
|
59
|
-
try {
|
|
60
|
-
if (!accountId) {
|
|
61
|
-
throw "account_id must be provided";
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
let url = `${this.unipileUrl}/linkedin/search?account_id=${accountId}&limit=${limit}`;
|
|
65
|
-
|
|
66
|
-
params["api"] = "classic";
|
|
67
|
-
params["category"] = "people";
|
|
68
|
-
|
|
69
|
-
if (cursor) {
|
|
70
|
-
url += `&cursor=${cursor}`;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return await post(url, params, this.headers);
|
|
74
|
-
} catch (error) {
|
|
75
|
-
throw error;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// preform a linkedin search by url
|
|
80
|
-
async searchByUrl({ accountId, searchUrl, cursor = "", limit = 10 }) {
|
|
81
|
-
try {
|
|
82
|
-
if (!accountId) {
|
|
83
|
-
throw "account_id must be provided";
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const body = {
|
|
87
|
-
api: "classic",
|
|
88
|
-
category: "people",
|
|
89
|
-
url: searchUrl,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
let url = `${this.unipileUrl}/linkedin/search?account_id=${accountId}&limit=${limit}`;
|
|
93
|
-
|
|
94
|
-
if (cursor) {
|
|
95
|
-
url += `&cursor=${cursor}`;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return await post(url, body, this.headers);
|
|
99
|
-
} catch (error) {
|
|
100
|
-
throw error;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// retrieve LinkedIn search parameters
|
|
105
|
-
async retrieveSearchParameters({
|
|
106
|
-
accountId,
|
|
107
|
-
type,
|
|
108
|
-
keywords = "",
|
|
109
|
-
limit = 3,
|
|
110
|
-
}) {
|
|
111
|
-
try {
|
|
112
|
-
if (!accountId) {
|
|
113
|
-
throw "account_id must be provided";
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const url = `${
|
|
117
|
-
this.unipileUrl
|
|
118
|
-
}/linkedin/search/parameters?account_id=${accountId}&keywords=${keywords}&type=${type.toUpperCase()}&limit=${limit}`;
|
|
119
|
-
|
|
120
|
-
return await get(url, this.headers);
|
|
121
|
-
} catch (error) {
|
|
122
|
-
throw error;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// retrieve LinkedIn profile
|
|
127
|
-
async retrieveProfile({
|
|
128
|
-
accountId,
|
|
129
|
-
profileId,
|
|
130
|
-
linkedinSections = [],
|
|
131
|
-
notify = false,
|
|
132
|
-
}) {
|
|
133
|
-
try {
|
|
134
|
-
if (!accountId) {
|
|
135
|
-
throw "account_id must be provided";
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (!profileId) {
|
|
139
|
-
throw "profile_id must be provided";
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
let url = `${this.unipileUrl}/users/${profileId}?account_id=${accountId}¬ify=${notify}`;
|
|
143
|
-
|
|
144
|
-
for (const linkedinSection of linkedinSections) {
|
|
145
|
-
url += `&linkedin_sections=${linkedinSection}`;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return await get(url, this.headers);
|
|
149
|
-
} catch (error) {
|
|
150
|
-
throw error;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// retrieve LinkedIn company by URL or ID
|
|
155
|
-
async retrieveCompany({ accountId, companyId, companyUrl }) {
|
|
156
|
-
try {
|
|
157
|
-
if (!accountId) {
|
|
158
|
-
throw "account_id must be provided";
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
let id = companyId;
|
|
162
|
-
if (!id && companyUrl) {
|
|
163
|
-
id = extractCompanyIdFromUrl(companyUrl);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (!id) {
|
|
167
|
-
throw "company_id or company_url must be provided";
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const url = `${this.unipileUrl}/linkedin/company/${id}?account_id=${accountId}`;
|
|
171
|
-
|
|
172
|
-
return await get(url, this.headers);
|
|
173
|
-
} catch (error) {
|
|
174
|
-
throw error;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// edit LinkedIn profile (headline, summary - JSON format)
|
|
179
|
-
async editProfile({ accountId, profileData }) {
|
|
180
|
-
try {
|
|
181
|
-
if (!accountId) {
|
|
182
|
-
throw "account_id must be provided";
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (!profileData || Object.keys(profileData).length === 0) {
|
|
186
|
-
throw "profileData must be provided with at least one field to update";
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const url = `${this.unipileUrl}/users/me/edit?account_id=${accountId}`;
|
|
190
|
-
|
|
191
|
-
const body = convertToBracketNotation(profileData);
|
|
192
|
-
|
|
193
|
-
return await patch(url, body, this.headers);
|
|
194
|
-
} catch (error) {
|
|
195
|
-
throw error;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// add experience position (multipart/form-data format with bracket notation)
|
|
200
|
-
async addExperience({ accountId, experience }) {
|
|
201
|
-
try {
|
|
202
|
-
if (!accountId) {
|
|
203
|
-
throw "account_id must be provided";
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (!experience || !experience.role || !experience.company) {
|
|
207
|
-
throw "experience with role and company is required";
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const url = `${this.unipileUrl}/users/me/edit?account_id=${accountId}`;
|
|
211
|
-
|
|
212
|
-
const formData = new FormData();
|
|
213
|
-
formData.append("type", "LINKEDIN");
|
|
214
|
-
formData.append("account_id", accountId);
|
|
215
|
-
|
|
216
|
-
// Experience fields use bracket notation
|
|
217
|
-
formData.append("experience[role]", experience.role);
|
|
218
|
-
formData.append("experience[company]", experience.company);
|
|
219
|
-
|
|
220
|
-
if (experience.description) {
|
|
221
|
-
formData.append("experience[description]", experience.description);
|
|
222
|
-
}
|
|
223
|
-
if (experience.notify_network !== undefined) {
|
|
224
|
-
formData.append("experience[notify_network]", experience.notify_network.toString());
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Start date with bracket notation
|
|
228
|
-
if (experience.start_month) {
|
|
229
|
-
formData.append("experience[seniority][start_date][month]", experience.start_month.toString());
|
|
230
|
-
}
|
|
231
|
-
if (experience.start_year) {
|
|
232
|
-
formData.append("experience[seniority][start_date][year]", experience.start_year.toString());
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// End date with bracket notation (optional - for non-current positions)
|
|
236
|
-
if (experience.end_month) {
|
|
237
|
-
formData.append("experience[seniority][end_date][month]", experience.end_month.toString());
|
|
238
|
-
}
|
|
239
|
-
if (experience.end_year) {
|
|
240
|
-
formData.append("experience[seniority][end_date][year]", experience.end_year.toString());
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Skills array - repeat key for each skill
|
|
244
|
-
if (experience.skills && Array.isArray(experience.skills)) {
|
|
245
|
-
for (const skill of experience.skills) {
|
|
246
|
-
formData.append("experience[skills]", skill);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Use formData.getHeaders() to get correct content-type with boundary
|
|
251
|
-
const headers = {
|
|
252
|
-
accept: "application/json",
|
|
253
|
-
"X-API-KEY": this.headers["X-API-KEY"],
|
|
254
|
-
...formData.getHeaders(),
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
return await patch(url, formData, headers);
|
|
258
|
-
} catch (error) {
|
|
259
|
-
throw error;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
package/src/utils/api.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
|
-
|
|
3
|
-
export const get = async (route, headers = {}) => {
|
|
4
|
-
try {
|
|
5
|
-
const response = await axios.get(route, { headers });
|
|
6
|
-
return response.data;
|
|
7
|
-
} catch (error) {
|
|
8
|
-
throw error;
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const post = async (route, body = {}, headers = {}) => {
|
|
13
|
-
try {
|
|
14
|
-
const response = await axios.post(route, body, { headers });
|
|
15
|
-
return response.data;
|
|
16
|
-
} catch (error) {
|
|
17
|
-
throw error;
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const patch = async (route, body = {}, headers = {}) => {
|
|
22
|
-
try {
|
|
23
|
-
const response = await axios.patch(route, body, { headers });
|
|
24
|
-
return response.data;
|
|
25
|
-
} catch (error) {
|
|
26
|
-
throw error;
|
|
27
|
-
}
|
|
28
|
-
};
|