Skip to content

Commit 587f267

Browse files
authored
Job Posting: New job posting endpoint (#373)
* New job posting endpoint * test fix * test fix2
1 parent 24ae6e2 commit 587f267

8 files changed

Lines changed: 233 additions & 3 deletions

File tree

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,44 @@ try {
298298
}
299299
```
300300

301+
**Using Job Posting Search API**
302+
303+
```js
304+
// By Search (Elasticsearch query)
305+
const esQuery = {
306+
query: {
307+
bool: {
308+
must: [
309+
{ term: { title_role: 'engineering' } },
310+
{ term: { remote_work_policy: 'remote' } },
311+
],
312+
},
313+
},
314+
}
315+
316+
try {
317+
const response = await PDLJSClient.jobPosting.search({ ...esQuery, size: 10 });
318+
319+
console.log(response.total);
320+
} catch (error) {
321+
console.log(error);
322+
}
323+
324+
// By Search (field parameters)
325+
try {
326+
const response = await PDLJSClient.jobPosting.search({
327+
title_role: 'engineering',
328+
remote_work_policy: 'remote',
329+
is_active: true,
330+
size: 10,
331+
});
332+
333+
console.log(response.total);
334+
} catch (error) {
335+
console.log(error);
336+
}
337+
```
338+
301339
**Using IP Enrichment API**
302340

303341
```js
@@ -427,6 +465,11 @@ try {
427465
| [Company Bulk Enrichment API](https://docs.peopledatalabs.com/docs/bulk-company-enrichment-api) | `PDLJS.company.bulk.enrichment({ ...records })` |
428466
| [Company Search API](https://docs.peopledatalabs.com/docs/company-search-api) | SQL: `PDLJS.company.search.sql({ ...params })` <br/> Elasticsearch: `PDLJS.company.search.elastic({ ...params })`|
429467

468+
**Job Posting Endpoints**
469+
| API Endpoint | PDLJS Function |
470+
|-|-|
471+
| [Job Posting Search API](https://docs.peopledatalabs.com/docs/job-posting-search-api) | `PDLJS.jobPosting.search({ ...params })` |
472+
430473
**Supporting Endpoints**
431474
| API Endpoint | PDLJS Function |
432475
|-|-|

src/endpoints/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import cleaner from './cleaner/index.js';
77
import enrichment from './enrichment/index.js';
88
import enrichmentPreview from './enrichmentPreview/index.js';
99
import identify from './identify/index.js';
10+
import jobPostingSearch from './jobPostingSearch/index.js';
1011
import jobTitle from './jobTitle/index.js';
1112
import retrieve from './retrieve/index.js';
1213
import search from './search/index.js';
1314

14-
export { autocomplete, bulkCompanyEnrichment, bulkEnrichment, bulkRetrieve, changelog, cleaner, enrichment, enrichmentPreview, identify, jobTitle, retrieve, search };
15+
export { autocomplete, bulkCompanyEnrichment, bulkEnrichment, bulkRetrieve, changelog, cleaner, enrichment, enrichmentPreview, identify, jobPostingSearch, jobTitle, retrieve, search };
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import axios from 'axios';
2+
3+
import { check, errorHandler } from '../../errors.js';
4+
import { RequestOptions } from '../../types/api-types.js';
5+
import { JobPostingSearchParams, JobPostingSearchResponse } from '../../types/jobPosting-types.js';
6+
import { parseRateLimitingResponse } from '../../utils/api-utils.js';
7+
import SDK_VERSION from '../../utils/sdk-version.js';
8+
9+
export default (
10+
basePath: string,
11+
apiKey: string,
12+
params: JobPostingSearchParams,
13+
options: RequestOptions = {},
14+
) => new Promise<JobPostingSearchResponse>((resolve, reject) => {
15+
check(params, basePath, apiKey, null, 'jobPostingSearch').then(() => {
16+
const body = {
17+
size: 10,
18+
pretty: false,
19+
...params,
20+
};
21+
22+
const headers = {
23+
'Content-Type': 'application/json',
24+
'Accept-Encoding': 'gzip',
25+
'User-Agent': 'PDL-JS-SDK',
26+
'SDK-Version': SDK_VERSION,
27+
'X-Api-Key': apiKey,
28+
};
29+
30+
axios.post<JobPostingSearchResponse>(`${basePath}/job_posting/search`, body, {
31+
headers,
32+
...options,
33+
}).then((response) => {
34+
if (response?.data?.status === 200) {
35+
resolve(parseRateLimitingResponse(response));
36+
}
37+
}).catch((error) => {
38+
reject(errorHandler(error));
39+
});
40+
}).catch((error) => {
41+
reject(error);
42+
});
43+
});

src/errors.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { AutoCompleteParams } from './types/autocomplete-types.js';
44
import { ChangelogParams } from './types/changelog-types.js';
55
import { ErrorEndpoint, PdlError } from './types/error-types.js';
66
import { IPParams } from './types/ip-types.js';
7+
import { JobPostingQuerySearchParams, JobPostingSearchParams } from './types/jobPosting-types.js';
78
import { JobTitleParams } from './types/jobTitle-types.js';
89
import { RetrieveParams } from './types/retrieve-types.js';
910
import { BaseSearchParams } from './types/search-types.js';
@@ -71,6 +72,22 @@ const check = (
7172
}
7273
}
7374

75+
if (endpoint === 'jobPostingSearch') {
76+
const searchParams = params as JobPostingSearchParams | undefined;
77+
if (!searchParams || Object.keys(searchParams).length === 0) {
78+
error.message = 'Missing Params';
79+
error.status = 400;
80+
reject(error);
81+
} else if (
82+
'query' in searchParams
83+
&& typeof (searchParams as JobPostingQuerySearchParams).query !== 'object'
84+
) {
85+
error.message = 'query must be an object';
86+
error.status = 400;
87+
reject(error);
88+
}
89+
}
90+
7491
if (endpoint === 'jobTitle') {
7592
const { jobTitle } = params as JobTitleParams;
7693
if (!jobTitle) {

src/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { autocomplete, bulkCompanyEnrichment, bulkEnrichment, bulkRetrieve, changelog, cleaner, enrichment, enrichmentPreview, identify, jobTitle, retrieve, search } from './endpoints/index.js';
1+
import { autocomplete, bulkCompanyEnrichment, bulkEnrichment, bulkRetrieve, changelog, cleaner, enrichment, enrichmentPreview, identify, jobPostingSearch, jobTitle, retrieve, search } from './endpoints/index.js';
22
import ip from './endpoints/ip/index.js';
33
import { APISettings, RequestOptions } from './types/api-types.js';
44
import { AutoCompleteParams, AutoCompleteResponse } from './types/autocomplete-types.js';
@@ -10,6 +10,7 @@ import { CompanyResponse, PersonResponse } from './types/common-types.js';
1010
import { CompanyEnrichmentParams, CompanyEnrichmentResponse, PersonEnrichmentParams, PersonEnrichmentPreviewParams, PersonEnrichmentPreviewResponse, PersonEnrichmentResponse, PersonPreviewResponse } from './types/enrichment-types.js';
1111
import { IdentifyParams, IdentifyResponse } from './types/identify-types.js';
1212
import { IPParams, IPResponse } from './types/ip-types.js';
13+
import { JobPostingSearchParams, JobPostingSearchResponse } from './types/jobPosting-types.js';
1314
import { JobTitleParams, JobTitleResponse } from './types/jobTitle-types.js';
1415
import { RetrieveParams, RetrieveResponse } from './types/retrieve-types.js';
1516
import { CompanySearchParams, CompanySearchResponse, PersonSearchParams, PersonSearchResponse } from './types/search-types.js';
@@ -57,6 +58,10 @@ class PDLJS {
5758

5859
public jobTitle: (params: JobTitleParams, options?: RequestOptions) => Promise<JobTitleResponse>;
5960

61+
public jobPosting: {
62+
search: (params: JobPostingSearchParams, options?: RequestOptions) => Promise<JobPostingSearchResponse>;
63+
};
64+
6065
public ip: (params: IPParams, options?: RequestOptions) => Promise<IPResponse>;
6166

6267
constructor({
@@ -109,6 +114,10 @@ class PDLJS {
109114

110115
this.jobTitle = (params: JobTitleParams, options) => jobTitle(this.basePath, this.apiKey, params, options);
111116

117+
this.jobPosting = {
118+
search: (params, options) => jobPostingSearch(this.basePath, this.apiKey, params, options),
119+
};
120+
112121
this.ip = (params: IPParams, options) => ip(this.basePath, this.apiKey, params, options);
113122
}
114123
}
@@ -138,6 +147,8 @@ export type {
138147
IdentifyResponse,
139148
IPParams,
140149
IPResponse,
150+
JobPostingSearchParams,
151+
JobPostingSearchResponse,
141152
JobTitleParams,
142153
JobTitleResponse,
143154
LocationCleanerParams,

src/types/error-types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { RateLimit } from './api-types.js';
22

3-
export type ErrorEndpoint = 'enrichment' | 'autocomplete' | 'changelog' | 'search' | 'identify' | 'bulk' | 'cleaner' | 'retrieve' | 'jobTitle' | 'ip';
3+
export type ErrorEndpoint = 'enrichment' | 'autocomplete' | 'changelog' | 'search' | 'identify' | 'bulk' | 'cleaner' | 'retrieve' | 'jobTitle' | 'jobPostingSearch' | 'ip';
44

55
export type PdlError = {
66
message: string;

src/types/jobPosting-types.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { BaseResponse } from './api-types.js';
2+
3+
export type RemoteWorkPolicy = 'remote' | 'onsite';
4+
export type SalaryPeriod = 'year' | 'month' | 'week' | 'day' | 'hour';
5+
6+
interface JobPostingSearchCommon {
7+
pretty?: boolean;
8+
scroll_token?: string;
9+
size?: number;
10+
}
11+
12+
export interface JobPostingQuerySearchParams extends JobPostingSearchCommon {
13+
query: object;
14+
}
15+
16+
export interface JobPostingParamSearchParams extends JobPostingSearchCommon {
17+
id?: string;
18+
first_seen_min?: string;
19+
first_seen_max?: string;
20+
deactivated_date_min?: string;
21+
deactivated_date_max?: string;
22+
title?: string;
23+
title_class?: string;
24+
title_role?: string;
25+
title_sub_role?: string;
26+
title_levels?: string;
27+
company_id?: string;
28+
company_name?: string;
29+
company_industry?: string;
30+
company_industry_v2?: string;
31+
company_website?: string;
32+
company_profile?: string;
33+
location?: string;
34+
description?: string;
35+
salary_range_min?: number;
36+
salary_range_max?: number;
37+
salary_currency?: string;
38+
salary_period?: SalaryPeriod;
39+
remote_work_policy?: RemoteWorkPolicy;
40+
inferred_skills?: string;
41+
last_verified_min?: string;
42+
last_verified_max?: string;
43+
is_active?: boolean;
44+
}
45+
46+
export type JobPostingSearchParams = JobPostingQuerySearchParams | JobPostingParamSearchParams;
47+
48+
export interface JobPostingResponse {
49+
id?: string | null;
50+
[key: string]: unknown;
51+
}
52+
53+
export interface JobPostingSearchResponse extends BaseResponse {
54+
data: Array<JobPostingResponse>;
55+
scroll_token?: string;
56+
total: number;
57+
}

tests/index.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,64 @@ describe('Job Title API', () => {
550550
});
551551
});
552552

553+
describe('Job Posting Search API', () => {
554+
const jobPostingElastic = {
555+
query: {
556+
bool: {
557+
must: [
558+
{ term: { title_role: 'engineering' } },
559+
],
560+
},
561+
},
562+
};
563+
564+
it(`Should Return Job Posting Records for ${JSON.stringify(jobPostingElastic)}`, async () => {
565+
try {
566+
const response = await PDLJSClient.jobPosting.search({ ...jobPostingElastic, size: 10 });
567+
568+
expect(response.status).to.equal(200);
569+
expect(response).to.be.a('object');
570+
} catch (error) {
571+
expect(error).to.equal(null);
572+
}
573+
}).timeout(10000);
574+
575+
it('Should Return Job Posting Records by field params', async () => {
576+
try {
577+
const response = await PDLJSClient.jobPosting.search({
578+
title_role: 'engineering',
579+
remote_work_policy: 'remote',
580+
size: 10,
581+
});
582+
583+
expect(response.status).to.equal(200);
584+
expect(response).to.be.a('object');
585+
} catch (error) {
586+
expect(error).to.equal(null);
587+
}
588+
}).timeout(10000);
589+
590+
it('Should Error for Job Posting Search with no params', async () => {
591+
try {
592+
const response = await PDLJSClient.jobPosting.search();
593+
594+
expect(response).to.equal(null);
595+
} catch (error) {
596+
expect(error).to.be.a('object');
597+
}
598+
});
599+
600+
it('Should Error for Job Posting Search with empty params', async () => {
601+
try {
602+
const response = await PDLJSClient.jobPosting.search({});
603+
604+
expect(response).to.equal(null);
605+
} catch (error) {
606+
expect(error).to.be.a('object');
607+
}
608+
});
609+
});
610+
553611
describe('IP Enrichment API', () => {
554612
it(`Should Return IP Records for ${JSON.stringify(ip)}`, async () => {
555613
try {

0 commit comments

Comments
 (0)