greenhouse_io 1.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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