greenhouse_io 1.0.2 → 2.0.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +86 -27
- data/lib/greenhouse_io.rb +3 -0
- data/lib/greenhouse_io/api.rb +8 -61
- data/lib/greenhouse_io/api/client.rb +88 -0
- data/lib/greenhouse_io/api/job_board.rb +66 -0
- data/lib/greenhouse_io/configuration.rb +21 -0
- data/lib/greenhouse_io/error.rb +3 -15
- data/lib/greenhouse_io/version.rb +1 -1
- data/spec/fixtures/cassettes/client/activity_feed.yml +36 -0
- data/spec/fixtures/cassettes/client/application.yml +36 -0
- data/spec/fixtures/cassettes/client/applications.yml +43 -0
- data/spec/fixtures/cassettes/client/candidate.yml +36 -0
- data/spec/fixtures/cassettes/client/candidates.yml +38 -0
- data/spec/fixtures/cassettes/client/department.yml +36 -0
- data/spec/fixtures/cassettes/client/departments.yml +38 -0
- data/spec/fixtures/cassettes/client/job.yml +37 -0
- data/spec/fixtures/cassettes/client/job_post.yml +49 -0
- data/spec/fixtures/cassettes/client/jobs.yml +39 -0
- data/spec/fixtures/cassettes/client/office.yml +36 -0
- data/spec/fixtures/cassettes/client/offices.yml +46 -0
- data/spec/fixtures/cassettes/client/scheduled_interviews.yml +38 -0
- data/spec/fixtures/cassettes/client/scorecards.yml +37 -0
- data/spec/fixtures/cassettes/client/source.yml +39 -0
- data/spec/fixtures/cassettes/client/sources.yml +37 -0
- data/spec/fixtures/cassettes/client/stages.yml +36 -0
- data/spec/fixtures/cassettes/client/user.yml +36 -0
- data/spec/fixtures/cassettes/client/users.yml +38 -0
- data/spec/fixtures/cassettes/invalid_id.yml +1 -1
- data/spec/fixtures/cassettes/job.yml +1 -1
- data/spec/greenhouse_io/api/client_spec.rb +474 -0
- data/spec/{greenhouse/api_spec.rb → greenhouse_io/api/job_board_spec.rb} +10 -9
- data/spec/greenhouse_io/configuration_spec.rb +71 -0
- data/spec/{greenhouse → greenhouse_io}/error_spec.rb +0 -2
- data/spec/spec_helper.rb +5 -0
- metadata +51 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f601eaa22aed7becbf93fd8199fe6e48e9726ac
|
4
|
+
data.tar.gz: a346a9f3404a2029b9120222f443a3e1cc561373
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b7f32c167e8da28dc2590246f774878c33e939aa4a5a3209730d71f53e658790df0dca8a674fd8823eb71045da1ca8d839df912ca99ce8960ed207ee01ab540
|
7
|
+
data.tar.gz: ef43dfc2b60318b96b4ee1795da0c1e5adb7dde1511d392cae24c5c9d5cb963b98c1c3992717bb048e86ee24d82935713a992b2d6dade8851d35ba7269f7d9b3
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -12,75 +12,85 @@ API (requires Ruby 1.9.3 or greater).
|
|
12
12
|
|
13
13
|
## Installation
|
14
14
|
|
15
|
-
Add
|
15
|
+
Add the gem to your application's Gemfile:
|
16
16
|
|
17
17
|
gem 'greenhouse_io'
|
18
18
|
|
19
|
-
And then execute:
|
20
|
-
|
21
|
-
$ bundle
|
22
|
-
|
23
19
|
Or install it yourself as:
|
24
20
|
|
25
21
|
$ gem install greenhouse_io
|
22
|
+
|
23
|
+
## Configuration
|
26
24
|
|
27
|
-
|
25
|
+
You can assign default configuration values when using this gem.
|
26
|
+
Here is an example `config/initializers/greenhouse_io.rb` file used in a Rails application:
|
28
27
|
|
29
|
-
Creating an instance of the API client:
|
30
28
|
```ruby
|
31
|
-
|
29
|
+
GreenhouseIo.configure do |config|
|
30
|
+
config.symbolize_keys = true # set response keys as strings or symbols, default is false
|
31
|
+
config.organization = 'General Assembly'
|
32
|
+
config.api_token = ENV['GREENHOUSE_API_TOKEN']
|
33
|
+
end
|
32
34
|
```
|
33
35
|
|
34
|
-
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
Greenhouse's two APIs, **[Harvest](https://app.greenhouse.io/configure/dev_center/harvest)** and **[JobBoard](https://app.greenhouse.io/configure/dev_center/api_documentation)**, can now be accessed through the gem. The [`GreenhouseIo::JobBoard`](#greenhouseiojobboard) is nearly identical to the old `GreenhouseIo::API` class. [`GreenhouseIo::Client`](#greenhouseioclient) connects to the new Harvest API.
|
39
|
+
|
40
|
+
### GreenhouseIo::JobBoard
|
41
|
+
|
42
|
+
Creating an instance of the JobBoard client:
|
35
43
|
```ruby
|
36
|
-
gh = GreenhouseIo::
|
44
|
+
gh = GreenhouseIo::JobBoard.new("api_token", organization: "your_organization")
|
37
45
|
```
|
38
46
|
|
39
|
-
|
47
|
+
If you've configured the gem with a default `organization` and `api_token`, then you can just instantiate the class.
|
40
48
|
```ruby
|
41
|
-
gh = GreenhouseIo::
|
49
|
+
gh = GreenhouseIo::JobBoard.new
|
42
50
|
```
|
43
51
|
|
44
|
-
|
52
|
+
`api_token` is only required for `#apply_to_job` and `organization` is also optional during initialization if an organization is passed in during method requests.
|
53
|
+
|
54
|
+
#### Fetching Office Data
|
45
55
|
```ruby
|
46
56
|
gh.offices
|
47
|
-
gh.offices(:
|
57
|
+
gh.offices(organization: 'different_organization')
|
48
58
|
# returns a hash containing all of the organization's department and jobs grouped by office
|
49
59
|
```
|
50
60
|
|
51
61
|
```ruby
|
52
|
-
gh.office(
|
53
|
-
gh.office(
|
62
|
+
gh.office(id)
|
63
|
+
gh.office(id, organization: 'different_organization')
|
54
64
|
# returns a hash containing the departments and jobs of a specific office
|
55
65
|
```
|
56
66
|
|
57
|
-
|
67
|
+
#### Fetching Department Data
|
58
68
|
```ruby
|
59
69
|
gh.departments
|
60
|
-
gh.departments(:
|
70
|
+
gh.departments(organization: 'different_organizaton')
|
61
71
|
```
|
62
72
|
|
63
73
|
```ruby
|
64
|
-
gh.department(
|
65
|
-
gh.department(
|
74
|
+
gh.department(id)
|
75
|
+
gh.department(id, organization: 'different_organization')
|
66
76
|
```
|
67
77
|
|
68
|
-
|
78
|
+
#### Fetching Job Data
|
69
79
|
```ruby
|
70
80
|
gh.jobs
|
71
|
-
gh.jobs(:
|
81
|
+
gh.jobs(content: 'true')
|
72
82
|
# includes the job description in the response
|
73
|
-
gh.jobs(:
|
83
|
+
gh.jobs(organization: 'different_organization')
|
74
84
|
```
|
75
85
|
|
76
86
|
```ruby
|
77
|
-
gh.job(
|
78
|
-
gh.job(
|
87
|
+
gh.job(id)
|
88
|
+
gh.job(id, questions: true)
|
79
89
|
# returns the specified job and the array of questions on the application
|
80
|
-
gh.job(
|
90
|
+
gh.job(id, organization: 'different_organization')
|
81
91
|
```
|
82
92
|
|
83
|
-
|
93
|
+
#### Submitting a Job Application
|
84
94
|
This is the only API method that **requires** an API token from Greenhouse
|
85
95
|
```ruby
|
86
96
|
gh.apply_to_job(form_parameter_hash)
|
@@ -90,6 +100,55 @@ gh.apply_to_job(form_parameter_hash)
|
|
90
100
|
# has the value of the job ID on Greenhouse.io
|
91
101
|
```
|
92
102
|
|
103
|
+
### GreenhouseIo::Client
|
104
|
+
|
105
|
+
Creating an instance of the API client:
|
106
|
+
```ruby
|
107
|
+
gh_client = GreenhouseIo::Client.new("api_token")
|
108
|
+
```
|
109
|
+
|
110
|
+
If you've configured the gem with a default `api_token`, then you can just instantiate the class.
|
111
|
+
```ruby
|
112
|
+
gh_client = GreenhouseIo::Client.new
|
113
|
+
```
|
114
|
+
|
115
|
+
#### Throttling
|
116
|
+
|
117
|
+
Rate limit and rate limit remaining are availbe fter making an API request with an API client:
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
gh_client.rate_limit # => 20
|
121
|
+
gh_client.rate_limit_remaining # => 20
|
122
|
+
```
|
123
|
+
|
124
|
+
#### Pagination
|
125
|
+
|
126
|
+
All `GreenhouseIo::Client` API methods accept `:page` and `:per_page` options to get specific results of a paginated response from Greenhouse.
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
gh_client.offices(id, page: 1, per_page: 2)
|
130
|
+
```
|
131
|
+
|
132
|
+
#### Available methods
|
133
|
+
|
134
|
+
Methods in which an `id` is optional:
|
135
|
+
|
136
|
+
* `offices`
|
137
|
+
* `departments`
|
138
|
+
* `candidates`
|
139
|
+
* `applications`
|
140
|
+
* `jobs`
|
141
|
+
* `users`
|
142
|
+
* `sources`
|
143
|
+
|
144
|
+
Methods in which an `id` is **required**:
|
145
|
+
|
146
|
+
* `activity_feed` *(requires a candidate ID)*
|
147
|
+
* `scorecards` *(requires an application ID)*
|
148
|
+
* `scheduled_interviews` *(requires an application ID)*
|
149
|
+
* `stages` *(requires a job ID)*
|
150
|
+
* `job_post` *(requires a job ID)*
|
151
|
+
|
93
152
|
## Contributing
|
94
153
|
|
95
154
|
1. Fork it
|
data/lib/greenhouse_io.rb
CHANGED
data/lib/greenhouse_io/api.rb
CHANGED
@@ -1,72 +1,19 @@
|
|
1
1
|
module GreenhouseIo
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
include HTTMultiParty
|
6
|
-
base_uri 'https://api.greenhouse.io/v1'
|
7
|
-
|
8
|
-
def initialize(api_token = nil, default_options = {})
|
9
|
-
@api_token = api_token || ENV['GREENHOUSE_API_TOKEN']
|
10
|
-
@organization = default_options.delete(:organization)
|
11
|
-
end
|
12
|
-
|
13
|
-
def offices(options = {})
|
14
|
-
get_response_hash("/boards/#{ query_organization(options) }/embed/offices")
|
15
|
-
end
|
16
|
-
|
17
|
-
def office(id, options = {})
|
18
|
-
get_response_hash("/boards/#{ query_organization(options) }/embed/office?id=#{ id }")
|
19
|
-
end
|
20
|
-
|
21
|
-
def departments(options = {})
|
22
|
-
get_response_hash("/boards/#{ query_organization(options) }/embed/departments")
|
23
|
-
end
|
24
|
-
|
25
|
-
def department(id, options = {})
|
26
|
-
get_response_hash("/boards/#{ query_organization(options) }/embed/department?id=#{ id }")
|
2
|
+
module API
|
3
|
+
def get_response(url, options)
|
4
|
+
self.class.get(url, options)
|
27
5
|
end
|
28
6
|
|
29
|
-
def
|
30
|
-
|
7
|
+
def post_response(url, options)
|
8
|
+
self.class.post(url, options)
|
31
9
|
end
|
32
10
|
|
33
|
-
def
|
34
|
-
|
35
|
-
end
|
36
|
-
|
37
|
-
def apply_to_job(job_form_hash)
|
38
|
-
options = { :body => job_form_hash, :basic_auth => basic_auth }
|
39
|
-
post_response_hash('/applications', options)
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def query_organization(options_hash)
|
45
|
-
org = options_hash[:organization] || @organization
|
46
|
-
org.nil? ? (raise GreenhouseIo::Error.new("organization can't be blank")) : org
|
47
|
-
end
|
48
|
-
|
49
|
-
def get_response_hash(url)
|
50
|
-
response = self.class.get(url)
|
51
|
-
if response.code == 200
|
52
|
-
MultiJson.load(response.body, :symbolize_keys => true)
|
53
|
-
else
|
54
|
-
raise GreenhouseIo::Error.new(response.code)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def post_response_hash(url, options)
|
59
|
-
response = self.class.post(url, options)
|
60
|
-
if response.code == 200
|
61
|
-
response.include?("success") ? MultiJson.load(response.body, :symbolize_keys => true) : raise(GreenhouseIo::Error.new(response["reason"]))
|
62
|
-
else
|
63
|
-
raise GreenhouseIo::Error.new(response.code)
|
64
|
-
end
|
11
|
+
def parse_json(response)
|
12
|
+
MultiJson.load(response.body, symbolize_keys: GreenhouseIo.configuration.symbolize_keys)
|
65
13
|
end
|
66
14
|
|
67
15
|
def basic_auth
|
68
|
-
{ :username =>
|
16
|
+
{ :username => self.api_token }
|
69
17
|
end
|
70
|
-
|
71
18
|
end
|
72
19
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module GreenhouseIo
|
2
|
+
class Client
|
3
|
+
include HTTMultiParty
|
4
|
+
include GreenhouseIo::API
|
5
|
+
|
6
|
+
PERMITTED_OPTIONS = [:page, :per_page]
|
7
|
+
|
8
|
+
attr_accessor :api_token, :rate_limit, :rate_limit_remaining
|
9
|
+
base_uri 'https://harvest.greenhouse.io/v1'
|
10
|
+
|
11
|
+
def initialize(api_token = nil)
|
12
|
+
@api_token = api_token || GreenhouseIo.configuration.api_token
|
13
|
+
end
|
14
|
+
|
15
|
+
def offices(id = nil, options = {})
|
16
|
+
get_from_harvest_api "/offices#{path_id(id)}", options
|
17
|
+
end
|
18
|
+
|
19
|
+
def departments(id = nil, options = {})
|
20
|
+
get_from_harvest_api "/departments#{path_id(id)}", options
|
21
|
+
end
|
22
|
+
|
23
|
+
def candidates(id = nil, options = {})
|
24
|
+
get_from_harvest_api "/candidates#{path_id(id)}", options
|
25
|
+
end
|
26
|
+
|
27
|
+
def activity_feed(id, options = {})
|
28
|
+
get_from_harvest_api "/candidates/#{id}/activity_feed", options
|
29
|
+
end
|
30
|
+
|
31
|
+
def applications(id = nil, options = {})
|
32
|
+
get_from_harvest_api "/applications#{path_id(id)}", options
|
33
|
+
end
|
34
|
+
|
35
|
+
def scorecards(id, options = {})
|
36
|
+
get_from_harvest_api "/applications/#{id}/scorecards", options
|
37
|
+
end
|
38
|
+
|
39
|
+
def scheduled_interviews(id, options = {})
|
40
|
+
get_from_harvest_api "/applications/#{id}/scheduled_interviews", options
|
41
|
+
end
|
42
|
+
|
43
|
+
def jobs(id = nil, options = {})
|
44
|
+
get_from_harvest_api "/jobs#{path_id(id)}", options
|
45
|
+
end
|
46
|
+
|
47
|
+
def stages(id, options = {})
|
48
|
+
get_from_harvest_api "/jobs/#{id}/stages", options
|
49
|
+
end
|
50
|
+
|
51
|
+
def job_post(id, options = {})
|
52
|
+
get_from_harvest_api "/jobs/#{id}/job_post", options
|
53
|
+
end
|
54
|
+
|
55
|
+
def users(id = nil, options = {})
|
56
|
+
get_from_harvest_api "/users#{path_id(id)}", options
|
57
|
+
end
|
58
|
+
|
59
|
+
def sources(id = nil, options = {})
|
60
|
+
get_from_harvest_api "/sources#{path_id(id)}", options
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def path_id(id = nil)
|
66
|
+
"/#{id}" unless id.nil?
|
67
|
+
end
|
68
|
+
|
69
|
+
def permitted_options(options)
|
70
|
+
options.select { |key, value| PERMITTED_OPTIONS.include? key }
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_from_harvest_api(url, options = {})
|
74
|
+
response = get_response(url, query: permitted_options(options), basic_auth: basic_auth)
|
75
|
+
set_rate_limits(response.headers)
|
76
|
+
if response.code == 200
|
77
|
+
parse_json(response)
|
78
|
+
else
|
79
|
+
raise GreenhouseIo::Error.new(response.code)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_rate_limits(headers)
|
84
|
+
self.rate_limit = headers['x-ratelimit-limit'].to_i
|
85
|
+
self.rate_limit_remaining = headers['x-ratelimit-remaining'].to_i
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module GreenhouseIo
|
2
|
+
class JobBoard
|
3
|
+
include HTTMultiParty
|
4
|
+
include GreenhouseIo::API
|
5
|
+
attr_accessor :api_token, :organization
|
6
|
+
base_uri 'https://api.greenhouse.io/v1'
|
7
|
+
|
8
|
+
def initialize(api_token = nil, default_options = {})
|
9
|
+
@api_token = api_token || GreenhouseIo.configuration.api_token
|
10
|
+
@organization = default_options.delete(:organization) || GreenhouseIo.configuration.organization
|
11
|
+
end
|
12
|
+
|
13
|
+
def offices(options = {})
|
14
|
+
get_from_job_board_api("/boards/#{ query_organization(options) }/embed/offices")
|
15
|
+
end
|
16
|
+
|
17
|
+
def office(id, options = {})
|
18
|
+
get_from_job_board_api("/boards/#{ query_organization(options) }/embed/office", query: { id: id })
|
19
|
+
end
|
20
|
+
|
21
|
+
def departments(options = {})
|
22
|
+
get_from_job_board_api("/boards/#{ query_organization(options) }/embed/departments")
|
23
|
+
end
|
24
|
+
|
25
|
+
def department(id, options = {})
|
26
|
+
get_from_job_board_api("/boards/#{ query_organization(options) }/embed/department", query: { id: id })
|
27
|
+
end
|
28
|
+
|
29
|
+
def jobs(options = {})
|
30
|
+
get_from_job_board_api("/boards/#{ query_organization(options) }/embed/jobs", query: { content: options[:content] })
|
31
|
+
end
|
32
|
+
|
33
|
+
def job(id, options = {})
|
34
|
+
get_from_job_board_api("/boards/#{ query_organization(options) }/embed/job", query: { id: id, questions: options[:questions] })
|
35
|
+
end
|
36
|
+
|
37
|
+
def apply_to_job(job_form_hash)
|
38
|
+
post_to_job_board_api('/applications', { :body => job_form_hash, :basic_auth => basic_auth })
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def query_organization(options_hash)
|
44
|
+
org = options_hash[:organization] || @organization
|
45
|
+
org.nil? ? (raise GreenhouseIo::Error.new("organization can't be blank")) : org
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_from_job_board_api(url, options = {})
|
49
|
+
response = get_response(url, options)
|
50
|
+
if response.code == 200
|
51
|
+
parse_json(response)
|
52
|
+
else
|
53
|
+
raise GreenhouseIo::Error.new(response.code)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def post_to_job_board_api(url, options)
|
58
|
+
response = post_response(url, options)
|
59
|
+
if response.code == 200
|
60
|
+
response.include?("success") ? parse_json(response) : raise(GreenhouseIo::Error.new(response["reason"]))
|
61
|
+
else
|
62
|
+
raise GreenhouseIo::Error.new(response.code)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GreenhouseIo
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :symbolize_keys, :organization, :api_token
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@symbolize_keys = false
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configuration
|
11
|
+
@configuration ||= Configuration.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.configuration=(config)
|
15
|
+
@configuration = config
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.configure
|
19
|
+
yield configuration
|
20
|
+
end
|
21
|
+
end
|