Skip to content

Commit 81ad322

Browse files
committed
[Components] homerun - new components
1 parent b38f664 commit 81ad322

File tree

12 files changed

+631
-9
lines changed

12 files changed

+631
-9
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import app from "../../homerun.app.mjs";
2+
3+
export default {
4+
key: "homerun-add-candidate-note",
5+
name: "Add Candidate Note",
6+
description: "Adds a note to a candidate's profile in Homerun. [See the documentation](https://developers.homerun.co/#tag/Job-Application-Notes/operation/job-applications.job-application-id.notes.post).",
7+
version: "0.0.1",
8+
type: "action",
9+
props: {
10+
app,
11+
jobApplicationId: {
12+
propDefinition: [
13+
app,
14+
"jobApplicationId",
15+
],
16+
},
17+
note: {
18+
type: "string",
19+
label: "Note Content",
20+
description: "The content of the note to add.",
21+
},
22+
displayName: {
23+
type: "string",
24+
label: "Display Name",
25+
description: "Name of the note's author.",
26+
},
27+
},
28+
methods: {
29+
addCandidateNote({
30+
jobApplicationId, ...args
31+
} = {}) {
32+
return this.app.post({
33+
path: `/job-applications/${jobApplicationId}/notes`,
34+
...args,
35+
});
36+
},
37+
},
38+
async run({ $ }) {
39+
const {
40+
addCandidateNote,
41+
jobApplicationId,
42+
note,
43+
displayName,
44+
} = this;
45+
46+
const response = await addCandidateNote({
47+
$,
48+
jobApplicationId,
49+
data: {
50+
note,
51+
display_name: displayName,
52+
},
53+
});
54+
55+
$.export("$summary", "Successfully added a note.");
56+
return response;
57+
},
58+
};
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import app from "../../homerun.app.mjs";
2+
3+
export default {
4+
key: "homerun-create-job-application",
5+
name: "Create Job Application",
6+
description: "Creates a new job application. [See the documentation](https://developers.homerun.co/#tag/Job-Applications/operation/job-applications.post).",
7+
version: "0.0.1",
8+
type: "action",
9+
props: {
10+
app,
11+
firstName: {
12+
type: "string",
13+
label: "First Name",
14+
description: "The first name of the applicant. Make sure you don't include numbers or special characters.",
15+
},
16+
lastName: {
17+
type: "string",
18+
label: "Last Name",
19+
description: "The last name of the applicant. Make sure you don't include numbers or special characters.",
20+
},
21+
email: {
22+
type: "string",
23+
label: "Email",
24+
description: "The email of the applicant.",
25+
},
26+
dateOfBirth: {
27+
type: "string",
28+
label: "Date of Birth",
29+
description: "The date of birth of the applicant in the format of `YYYY-MM-DD`.",
30+
optional: true,
31+
},
32+
vacancyId: {
33+
optional: true,
34+
propDefinition: [
35+
app,
36+
"vacancyId",
37+
],
38+
},
39+
phoneNumber: {
40+
type: "string",
41+
label: "Phone Number",
42+
description: "The phone number of the applicant.",
43+
optional: true,
44+
},
45+
photo: {
46+
type: "string",
47+
label: "Photo",
48+
description: "The URL of the applicant's photo.",
49+
optional: true,
50+
},
51+
experience: {
52+
type: "string",
53+
label: "Experience",
54+
description: "The experience of the applicant.",
55+
optional: true,
56+
},
57+
education: {
58+
type: "string",
59+
label: "Education",
60+
description: "The education of the applicant.",
61+
optional: true,
62+
},
63+
facebook: {
64+
type: "string",
65+
label: "Facebook",
66+
description: "The Facebook URL of the applicant. eg. `https://facebook.com/username`",
67+
optional: true,
68+
},
69+
twitter: {
70+
type: "string",
71+
label: "X",
72+
description: "The X URL of the applicant. eg. `https://x.com/username`",
73+
optional: true,
74+
},
75+
linkedin: {
76+
type: "string",
77+
label: "LinkedIn",
78+
description: "The LinkedIn URL of the applicant. eg. `https://linkedin.com/in/username`",
79+
optional: true,
80+
},
81+
github: {
82+
type: "string",
83+
label: "GitHub",
84+
description: "The GitHub URL of the applicant. eg. `https://github.com/username`",
85+
optional: true,
86+
},
87+
website: {
88+
type: "string",
89+
label: "Website",
90+
description: "The website URL of the applicant. eg. `https://username.com`",
91+
optional: true,
92+
},
93+
},
94+
methods: {
95+
createJobApplication(args = {}) {
96+
return this.app.post({
97+
path: "/job-applications",
98+
...args,
99+
});
100+
},
101+
},
102+
async run({ $ }) {
103+
const {
104+
createJobApplication,
105+
firstName,
106+
lastName,
107+
email,
108+
dateOfBirth,
109+
vacancyId,
110+
phoneNumber,
111+
photo,
112+
experience,
113+
education,
114+
facebook,
115+
twitter,
116+
linkedin,
117+
github,
118+
website,
119+
} = this;
120+
121+
const response = await createJobApplication({
122+
$,
123+
data: {
124+
vacancyId,
125+
first_name: firstName,
126+
last_name: lastName,
127+
email,
128+
date_of_birth: dateOfBirth,
129+
phone_number: phoneNumber,
130+
photo,
131+
experience,
132+
education,
133+
facebook,
134+
twitter,
135+
linkedin,
136+
github,
137+
website,
138+
},
139+
});
140+
141+
$.export("$summary", "Successfully created a job application.");
142+
return response;
143+
},
144+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const BASE_URL = "https://api.homerun.co";
2+
const VERSION_PATH = "/v2";
3+
const DEFAULT_LIMIT = 100;
4+
const DEFAULT_MAX = 1000;
5+
6+
const LAST_DATE_AT = "lastDateAt";
7+
8+
export default {
9+
BASE_URL,
10+
VERSION_PATH,
11+
DEFAULT_LIMIT,
12+
DEFAULT_MAX,
13+
LAST_DATE_AT,
14+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
async function iterate(iterations) {
2+
const items = [];
3+
for await (const item of iterations) {
4+
items.push(item);
5+
}
6+
return items;
7+
}
8+
9+
function getNestedProperty(obj, propertyString) {
10+
const properties = propertyString.split(".");
11+
return properties.reduce((prev, curr) => prev?.[curr], obj);
12+
}
13+
14+
export default {
15+
iterate,
16+
getNestedProperty,
17+
};

components/homerun/homerun.app.mjs

Lines changed: 136 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,143 @@
1+
import { axios } from "@pipedream/platform";
2+
import constants from "./common/constants.mjs";
3+
import utils from "./common/utils.mjs";
4+
15
export default {
26
type: "app",
37
app: "homerun",
4-
propDefinitions: {},
8+
propDefinitions: {
9+
vacancyId: {
10+
type: "string",
11+
label: "Vacancy ID",
12+
description: "The ID of the vacancy.",
13+
async options({ page }) {
14+
const { data } = await this.listVacancies({
15+
params: {
16+
page: page + 1,
17+
},
18+
});
19+
return data.map(({
20+
id: value,
21+
title: label,
22+
}) => ({
23+
label,
24+
value,
25+
}));
26+
},
27+
},
28+
jobApplicationId: {
29+
type: "string",
30+
label: "Job Application ID",
31+
description: "The ID of the job application.",
32+
async options({ page }) {
33+
const { data } = await this.listJobApplications({
34+
params: {
35+
page: page + 1,
36+
},
37+
});
38+
return data.map(({
39+
id: value,
40+
personal_info: {
41+
first_name: firstName,
42+
last_name: lastName,
43+
email,
44+
},
45+
}) => ({
46+
label: `${firstName} ${lastName} (${email})`,
47+
value,
48+
}));
49+
},
50+
},
51+
},
552
methods: {
6-
// this.$auth contains connected account data
7-
authKeys() {
8-
console.log(Object.keys(this.$auth));
53+
getUrl(path) {
54+
return `${constants.BASE_URL}${constants.VERSION_PATH}${path}`;
55+
},
56+
getHeaders(headers) {
57+
return {
58+
Authorization: `Bearer ${this.$auth.api_key}`,
59+
...headers,
60+
};
61+
},
62+
_makeRequest({
63+
$ = this, path, headers, ...args
64+
} = {}) {
65+
return axios($, {
66+
...args,
67+
url: this.getUrl(path),
68+
headers: this.getHeaders(headers),
69+
});
70+
},
71+
post(args = {}) {
72+
return this._makeRequest({
73+
method: "POST",
74+
...args,
75+
});
76+
},
77+
listVacancies(args = {}) {
78+
return this._makeRequest({
79+
path: "/vacancies",
80+
...args,
81+
});
82+
},
83+
listJobApplications(args = {}) {
84+
return this._makeRequest({
85+
path: "/job-applications",
86+
...args,
87+
});
88+
},
89+
async *getIterations({
90+
resourcesFn, resourcesFnArgs, resourceName,
91+
lastDateAt, dateField,
92+
max = constants.DEFAULT_MAX,
93+
}) {
94+
let page = 1;
95+
let resourcesCount = 0;
96+
97+
while (true) {
98+
const response =
99+
await resourcesFn({
100+
...resourcesFnArgs,
101+
params: {
102+
...resourcesFnArgs?.params,
103+
page,
104+
perPage: constants.DEFAULT_LIMIT,
105+
},
106+
});
107+
108+
const nextResources = utils.getNestedProperty(response, resourceName);
109+
110+
if (!nextResources?.length) {
111+
console.log("No more resources found");
112+
return;
113+
}
114+
115+
for (const resource of nextResources) {
116+
const isDateGreaterThanLasDate =
117+
lastDateAt
118+
&& Date.parse(resource[dateField]) > Date.parse(lastDateAt);
119+
120+
if (!lastDateAt || isDateGreaterThanLasDate) {
121+
yield resource;
122+
resourcesCount += 1;
123+
}
124+
125+
if (resourcesCount >= max) {
126+
console.log("Reached max resources");
127+
return;
128+
}
129+
}
130+
131+
if (nextResources.length < constants.DEFAULT_LIMIT) {
132+
console.log("No next page found");
133+
return;
134+
}
135+
136+
page += 1;
137+
}
138+
},
139+
paginate(args = {}) {
140+
return utils.iterate(this.getIterations(args));
9141
},
10142
},
11143
};

0 commit comments

Comments
 (0)