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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +86 -27
  4. data/lib/greenhouse_io.rb +3 -0
  5. data/lib/greenhouse_io/api.rb +8 -61
  6. data/lib/greenhouse_io/api/client.rb +88 -0
  7. data/lib/greenhouse_io/api/job_board.rb +66 -0
  8. data/lib/greenhouse_io/configuration.rb +21 -0
  9. data/lib/greenhouse_io/error.rb +3 -15
  10. data/lib/greenhouse_io/version.rb +1 -1
  11. data/spec/fixtures/cassettes/client/activity_feed.yml +36 -0
  12. data/spec/fixtures/cassettes/client/application.yml +36 -0
  13. data/spec/fixtures/cassettes/client/applications.yml +43 -0
  14. data/spec/fixtures/cassettes/client/candidate.yml +36 -0
  15. data/spec/fixtures/cassettes/client/candidates.yml +38 -0
  16. data/spec/fixtures/cassettes/client/department.yml +36 -0
  17. data/spec/fixtures/cassettes/client/departments.yml +38 -0
  18. data/spec/fixtures/cassettes/client/job.yml +37 -0
  19. data/spec/fixtures/cassettes/client/job_post.yml +49 -0
  20. data/spec/fixtures/cassettes/client/jobs.yml +39 -0
  21. data/spec/fixtures/cassettes/client/office.yml +36 -0
  22. data/spec/fixtures/cassettes/client/offices.yml +46 -0
  23. data/spec/fixtures/cassettes/client/scheduled_interviews.yml +38 -0
  24. data/spec/fixtures/cassettes/client/scorecards.yml +37 -0
  25. data/spec/fixtures/cassettes/client/source.yml +39 -0
  26. data/spec/fixtures/cassettes/client/sources.yml +37 -0
  27. data/spec/fixtures/cassettes/client/stages.yml +36 -0
  28. data/spec/fixtures/cassettes/client/user.yml +36 -0
  29. data/spec/fixtures/cassettes/client/users.yml +38 -0
  30. data/spec/fixtures/cassettes/invalid_id.yml +1 -1
  31. data/spec/fixtures/cassettes/job.yml +1 -1
  32. data/spec/greenhouse_io/api/client_spec.rb +474 -0
  33. data/spec/{greenhouse/api_spec.rb → greenhouse_io/api/job_board_spec.rb} +10 -9
  34. data/spec/greenhouse_io/configuration_spec.rb +71 -0
  35. data/spec/{greenhouse → greenhouse_io}/error_spec.rb +0 -2
  36. data/spec/spec_helper.rb +5 -0
  37. metadata +51 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 96d09bdfaaaac579b55d887e19e8841464b5c260
4
- data.tar.gz: a602434a5d159890c66d409e9ce5b84d0cd3cbba
3
+ metadata.gz: 9f601eaa22aed7becbf93fd8199fe6e48e9726ac
4
+ data.tar.gz: a346a9f3404a2029b9120222f443a3e1cc561373
5
5
  SHA512:
6
- metadata.gz: 6cc57641acc11a17e7dd119cfadecd76a698aec9cd80ddf281a15a84764941f34c0f8ea7c1140c1d9f33aa6a55adad247f52fd77429d85b34f122cc07d9f2731
7
- data.tar.gz: 15447a18d502738a6181ce8b7883f10739931fe15fe01466f2430465f655b450ba2db0ba96526e685ff587a036c014fb45ada43d884a4f246d52b3f6332db3ef
6
+ metadata.gz: 9b7f32c167e8da28dc2590246f774878c33e939aa4a5a3209730d71f53e658790df0dca8a674fd8823eb71045da1ca8d839df912ca99ce8960ed207ee01ab540
7
+ data.tar.gz: ef43dfc2b60318b96b4ee1795da0c1e5adb7dde1511d392cae24c5c9d5cb963b98c1c3992717bb048e86ee24d82935713a992b2d6dade8851d35ba7269f7d9b3
data/.gitignore CHANGED
@@ -13,3 +13,4 @@ pkg
13
13
  rdoc
14
14
  spec/reports
15
15
  tmp
16
+ .ruby-*
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 this line to your application's Gemfile:
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
- ## Usage
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
- gh = GreenhouseIo::API.new("api_token")
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
- The client will also use the environment variable `'GREENHOUSE_API_TOKEN'` by default:
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::API.new
44
+ gh = GreenhouseIo::JobBoard.new("api_token", organization: "your_organization")
37
45
  ```
38
46
 
39
- A default organization can be passed through an options hash:
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::API.new("api_token", :organization => "your_organization")
49
+ gh = GreenhouseIo::JobBoard.new
42
50
  ```
43
51
 
44
- ### Fetching Office Data
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(:organization => 'different_organization')
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(officeID)
53
- gh.office(officeID, :organization => 'different_organization')
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
- ### Fetching Department Data
67
+ #### Fetching Department Data
58
68
  ```ruby
59
69
  gh.departments
60
- gh.departments(:organization => 'different_organizaton')
70
+ gh.departments(organization: 'different_organizaton')
61
71
  ```
62
72
 
63
73
  ```ruby
64
- gh.department(departmentID)
65
- gh.department(departmentID, :organization => 'different_organization')
74
+ gh.department(id)
75
+ gh.department(id, organization: 'different_organization')
66
76
  ```
67
77
 
68
- ### Fetching Job Data
78
+ #### Fetching Job Data
69
79
  ```ruby
70
80
  gh.jobs
71
- gh.jobs(:content => 'true')
81
+ gh.jobs(content: 'true')
72
82
  # includes the job description in the response
73
- gh.jobs(:organization => 'different_organization')
83
+ gh.jobs(organization: 'different_organization')
74
84
  ```
75
85
 
76
86
  ```ruby
77
- gh.job(jobID)
78
- gh.job(jobID, :questions => true)
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(jobID, :organization => 'different_organization')
90
+ gh.job(id, organization: 'different_organization')
81
91
  ```
82
92
 
83
- ### Submitting a Job Application
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
@@ -4,4 +4,7 @@ require 'cgi'
4
4
 
5
5
  require "greenhouse_io/version"
6
6
  require "greenhouse_io/error"
7
+ require "greenhouse_io/configuration"
7
8
  require "greenhouse_io/api"
9
+ require "greenhouse_io/api/job_board"
10
+ require "greenhouse_io/api/client"
@@ -1,72 +1,19 @@
1
1
  module GreenhouseIo
2
- class API
3
- attr_accessor :api_token, :organization
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 jobs(options = {})
30
- get_response_hash("/boards/#{ query_organization(options) }/embed/jobs?content=#{ options[:content] }")
7
+ def post_response(url, options)
8
+ self.class.post(url, options)
31
9
  end
32
10
 
33
- def job(id, options = { :questions => false })
34
- get_response_hash("/boards/#{ query_organization(options) }/embed/job?id=#{ id }&questions=#{ options[:questions] }")
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 => @api_token }
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