open_api_smart_recruiters_sdk 0.2.1

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 (238) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +13 -0
  4. data/Gemfile +16 -0
  5. data/Gemfile.lock +65 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +35 -0
  8. data/Rakefile +12 -0
  9. data/lib/smart_recruiters/api/application_api_api.rb +188 -0
  10. data/lib/smart_recruiters/api/candidates_api.rb +2417 -0
  11. data/lib/smart_recruiters/api/configuration_api.rb +2504 -0
  12. data/lib/smart_recruiters/api/job_applications_api.rb +63 -0
  13. data/lib/smart_recruiters/api/jobs_api.rb +1851 -0
  14. data/lib/smart_recruiters/api/subscriptions_api.rb +469 -0
  15. data/lib/smart_recruiters/api_client.rb +379 -0
  16. data/lib/smart_recruiters/api_error.rb +46 -0
  17. data/lib/smart_recruiters/authorization.rb +66 -0
  18. data/lib/smart_recruiters/configuration.rb +214 -0
  19. data/lib/smart_recruiters/models/action.rb +248 -0
  20. data/lib/smart_recruiters/models/actions.rb +190 -0
  21. data/lib/smart_recruiters/models/all_of_candidate_details_primary_assignment.rb +262 -0
  22. data/lib/smart_recruiters/models/all_of_candidate_details_secondary_assignments_items.rb +262 -0
  23. data/lib/smart_recruiters/models/answer_field.rb +227 -0
  24. data/lib/smart_recruiters/models/answer_field_value.rb +216 -0
  25. data/lib/smart_recruiters/models/answer_record.rb +197 -0
  26. data/lib/smart_recruiters/models/application.rb +260 -0
  27. data/lib/smart_recruiters/models/application_attachment_webhook_payload.rb +248 -0
  28. data/lib/smart_recruiters/models/application_status_info.rb +231 -0
  29. data/lib/smart_recruiters/models/application_url.rb +198 -0
  30. data/lib/smart_recruiters/models/application_webhook_payload.rb +230 -0
  31. data/lib/smart_recruiters/models/apply_api_error.rb +259 -0
  32. data/lib/smart_recruiters/models/apply_api_request.rb +386 -0
  33. data/lib/smart_recruiters/models/apply_api_response.rb +224 -0
  34. data/lib/smart_recruiters/models/apply_configuration.rb +229 -0
  35. data/lib/smart_recruiters/models/apply_settings.rb +197 -0
  36. data/lib/smart_recruiters/models/assignment.rb +251 -0
  37. data/lib/smart_recruiters/models/assignment_actions.rb +197 -0
  38. data/lib/smart_recruiters/models/attachment.rb +230 -0
  39. data/lib/smart_recruiters/models/attachment_actions.rb +202 -0
  40. data/lib/smart_recruiters/models/attachments.rb +218 -0
  41. data/lib/smart_recruiters/models/callback_authentication.rb +188 -0
  42. data/lib/smart_recruiters/models/callback_basic_auth.rb +249 -0
  43. data/lib/smart_recruiters/models/callback_header_auth.rb +250 -0
  44. data/lib/smart_recruiters/models/callback_o_auth2_auth.rb +262 -0
  45. data/lib/smart_recruiters/models/callback_request.rb +216 -0
  46. data/lib/smart_recruiters/models/callback_request_request_details.rb +219 -0
  47. data/lib/smart_recruiters/models/callback_request_status.rb +19 -0
  48. data/lib/smart_recruiters/models/callback_requests.rb +192 -0
  49. data/lib/smart_recruiters/models/callback_url.rb +188 -0
  50. data/lib/smart_recruiters/models/candidate.rb +349 -0
  51. data/lib/smart_recruiters/models/candidate_actions.rb +216 -0
  52. data/lib/smart_recruiters/models/candidate_attachment_for_job.rb +253 -0
  53. data/lib/smart_recruiters/models/candidate_attachment_for_job_actions.rb +216 -0
  54. data/lib/smart_recruiters/models/candidate_attachments_for_job.rb +218 -0
  55. data/lib/smart_recruiters/models/candidate_consent.rb +197 -0
  56. data/lib/smart_recruiters/models/candidate_consent_decisions.rb +213 -0
  57. data/lib/smart_recruiters/models/candidate_consent_status.rb +212 -0
  58. data/lib/smart_recruiters/models/candidate_details.rb +349 -0
  59. data/lib/smart_recruiters/models/candidate_details_actions.rb +229 -0
  60. data/lib/smart_recruiters/models/candidate_input.rb +308 -0
  61. data/lib/smart_recruiters/models/candidate_job.rb +216 -0
  62. data/lib/smart_recruiters/models/candidate_location.rb +251 -0
  63. data/lib/smart_recruiters/models/candidate_primary_assignment.rb +225 -0
  64. data/lib/smart_recruiters/models/candidate_primary_assignment_job.rb +230 -0
  65. data/lib/smart_recruiters/models/candidate_primary_assignment_job_actions.rb +202 -0
  66. data/lib/smart_recruiters/models/candidate_properties.rb +199 -0
  67. data/lib/smart_recruiters/models/candidate_property.rb +268 -0
  68. data/lib/smart_recruiters/models/candidate_property_actions.rb +202 -0
  69. data/lib/smart_recruiters/models/candidate_property_availability.rb +253 -0
  70. data/lib/smart_recruiters/models/candidate_property_definition.rb +233 -0
  71. data/lib/smart_recruiters/models/candidate_property_definition_list.rb +208 -0
  72. data/lib/smart_recruiters/models/candidate_property_filter.rb +215 -0
  73. data/lib/smart_recruiters/models/candidate_property_filter_values.rb +208 -0
  74. data/lib/smart_recruiters/models/candidate_property_input_value.rb +198 -0
  75. data/lib/smart_recruiters/models/candidate_property_type.rb +27 -0
  76. data/lib/smart_recruiters/models/candidate_property_value.rb +212 -0
  77. data/lib/smart_recruiters/models/candidate_property_value_label.rb +202 -0
  78. data/lib/smart_recruiters/models/candidate_property_value_list.rb +208 -0
  79. data/lib/smart_recruiters/models/candidate_secondary_assignments.rb +202 -0
  80. data/lib/smart_recruiters/models/candidate_source.rb +225 -0
  81. data/lib/smart_recruiters/models/candidate_status.rb +230 -0
  82. data/lib/smart_recruiters/models/candidate_status_enum.rb +26 -0
  83. data/lib/smart_recruiters/models/candidate_status_history_list.rb +208 -0
  84. data/lib/smart_recruiters/models/candidate_status_history_list_elem.rb +224 -0
  85. data/lib/smart_recruiters/models/candidate_tags.rb +204 -0
  86. data/lib/smart_recruiters/models/candidate_webhook_payload.rb +202 -0
  87. data/lib/smart_recruiters/models/candidates.rb +255 -0
  88. data/lib/smart_recruiters/models/company_configuration.rb +246 -0
  89. data/lib/smart_recruiters/models/compensation.rb +215 -0
  90. data/lib/smart_recruiters/models/consent_decision.rb +226 -0
  91. data/lib/smart_recruiters/models/consent_decision_status.rb +20 -0
  92. data/lib/smart_recruiters/models/consent_request.rb +203 -0
  93. data/lib/smart_recruiters/models/consent_request_scheduled.rb +250 -0
  94. data/lib/smart_recruiters/models/consent_request_scheduled_body.rb +197 -0
  95. data/lib/smart_recruiters/models/consent_request_unavailable.rb +245 -0
  96. data/lib/smart_recruiters/models/consent_requests_results.rb +204 -0
  97. data/lib/smart_recruiters/models/consent_scope.rb +22 -0
  98. data/lib/smart_recruiters/models/consent_status.rb +20 -0
  99. data/lib/smart_recruiters/models/department.rb +220 -0
  100. data/lib/smart_recruiters/models/departments.rb +208 -0
  101. data/lib/smart_recruiters/models/dependent_job_properties.rb +218 -0
  102. data/lib/smart_recruiters/models/dependent_job_properties_ids.rb +204 -0
  103. data/lib/smart_recruiters/models/dependent_job_property.rb +216 -0
  104. data/lib/smart_recruiters/models/dependent_job_property_value.rb +220 -0
  105. data/lib/smart_recruiters/models/dependent_job_property_values.rb +208 -0
  106. data/lib/smart_recruiters/models/dependent_job_property_values_relation.rb +218 -0
  107. data/lib/smart_recruiters/models/dependent_job_property_values_relations.rb +218 -0
  108. data/lib/smart_recruiters/models/education.rb +270 -0
  109. data/lib/smart_recruiters/models/email_address.rb +188 -0
  110. data/lib/smart_recruiters/models/employment.rb +261 -0
  111. data/lib/smart_recruiters/models/error.rb +211 -0
  112. data/lib/smart_recruiters/models/error_response.rb +208 -0
  113. data/lib/smart_recruiters/models/event.rb +206 -0
  114. data/lib/smart_recruiters/models/event_name.rb +64 -0
  115. data/lib/smart_recruiters/models/experience.rb +251 -0
  116. data/lib/smart_recruiters/models/field_value.rb +216 -0
  117. data/lib/smart_recruiters/models/headcount_patch_request.rb +197 -0
  118. data/lib/smart_recruiters/models/headcount_update_accepted.rb +231 -0
  119. data/lib/smart_recruiters/models/hiring_process.rb +224 -0
  120. data/lib/smart_recruiters/models/hiring_process_sub_statuses.rb +221 -0
  121. data/lib/smart_recruiters/models/hiring_processes.rb +218 -0
  122. data/lib/smart_recruiters/models/hiring_team_member.rb +220 -0
  123. data/lib/smart_recruiters/models/hiring_team_member_response.rb +215 -0
  124. data/lib/smart_recruiters/models/hiring_team_member_response_actions.rb +202 -0
  125. data/lib/smart_recruiters/models/hiring_team_member_role.rb +22 -0
  126. data/lib/smart_recruiters/models/hiring_team_members.rb +208 -0
  127. data/lib/smart_recruiters/models/id_attachments_body.rb +249 -0
  128. data/lib/smart_recruiters/models/identifiable.rb +202 -0
  129. data/lib/smart_recruiters/models/industry.rb +207 -0
  130. data/lib/smart_recruiters/models/job.rb +197 -0
  131. data/lib/smart_recruiters/models/job_ad.rb +206 -0
  132. data/lib/smart_recruiters/models/job_ad_content.rb +220 -0
  133. data/lib/smart_recruiters/models/job_ad_input.rb +206 -0
  134. data/lib/smart_recruiters/models/job_ad_item.rb +397 -0
  135. data/lib/smart_recruiters/models/job_ad_item_actions.rb +202 -0
  136. data/lib/smart_recruiters/models/job_ad_language.rb +202 -0
  137. data/lib/smart_recruiters/models/job_ad_postings_webhook_payload.rb +216 -0
  138. data/lib/smart_recruiters/models/job_ad_publication_scheduled.rb +231 -0
  139. data/lib/smart_recruiters/models/job_ad_section.rb +206 -0
  140. data/lib/smart_recruiters/models/job_ad_sections.rb +233 -0
  141. data/lib/smart_recruiters/models/job_ad_unpublish_scheduled.rb +231 -0
  142. data/lib/smart_recruiters/models/job_ad_videos.rb +199 -0
  143. data/lib/smart_recruiters/models/job_ad_webhook_payload.rb +216 -0
  144. data/lib/smart_recruiters/models/job_ads.rb +192 -0
  145. data/lib/smart_recruiters/models/job_application.rb +242 -0
  146. data/lib/smart_recruiters/models/job_application_status.rb +26 -0
  147. data/lib/smart_recruiters/models/job_approval.rb +217 -0
  148. data/lib/smart_recruiters/models/job_approval_position.rb +251 -0
  149. data/lib/smart_recruiters/models/job_approval_webhook_payload.rb +216 -0
  150. data/lib/smart_recruiters/models/job_details.rb +394 -0
  151. data/lib/smart_recruiters/models/job_details_actions.rb +233 -0
  152. data/lib/smart_recruiters/models/job_id_attachments_body.rb +249 -0
  153. data/lib/smart_recruiters/models/job_input.rb +341 -0
  154. data/lib/smart_recruiters/models/job_job.rb +215 -0
  155. data/lib/smart_recruiters/models/job_job_actions.rb +211 -0
  156. data/lib/smart_recruiters/models/job_location_input.rb +252 -0
  157. data/lib/smart_recruiters/models/job_note.rb +202 -0
  158. data/lib/smart_recruiters/models/job_position.rb +256 -0
  159. data/lib/smart_recruiters/models/job_position_input.rb +282 -0
  160. data/lib/smart_recruiters/models/job_positions.rb +218 -0
  161. data/lib/smart_recruiters/models/job_property.rb +225 -0
  162. data/lib/smart_recruiters/models/job_property_definition.rb +300 -0
  163. data/lib/smart_recruiters/models/job_property_definition_creation_request.rb +282 -0
  164. data/lib/smart_recruiters/models/job_property_definition_list.rb +208 -0
  165. data/lib/smart_recruiters/models/job_property_input.rb +220 -0
  166. data/lib/smart_recruiters/models/job_property_value.rb +206 -0
  167. data/lib/smart_recruiters/models/job_property_value_definition.rb +220 -0
  168. data/lib/smart_recruiters/models/job_property_value_definition_list.rb +208 -0
  169. data/lib/smart_recruiters/models/job_status.rb +25 -0
  170. data/lib/smart_recruiters/models/job_status_history.rb +216 -0
  171. data/lib/smart_recruiters/models/job_status_history_actions.rb +202 -0
  172. data/lib/smart_recruiters/models/job_status_history_content.rb +220 -0
  173. data/lib/smart_recruiters/models/job_status_update.rb +202 -0
  174. data/lib/smart_recruiters/models/job_summary.rb +302 -0
  175. data/lib/smart_recruiters/models/job_webhook_payload.rb +202 -0
  176. data/lib/smart_recruiters/models/jobs.rb +235 -0
  177. data/lib/smart_recruiters/models/json_patch.rb +192 -0
  178. data/lib/smart_recruiters/models/json_pointer.rb +188 -0
  179. data/lib/smart_recruiters/models/label_translation.rb +216 -0
  180. data/lib/smart_recruiters/models/label_translations.rb +192 -0
  181. data/lib/smart_recruiters/models/labeled.rb +202 -0
  182. data/lib/smart_recruiters/models/language.rb +220 -0
  183. data/lib/smart_recruiters/models/language_code.rb +87 -0
  184. data/lib/smart_recruiters/models/location.rb +251 -0
  185. data/lib/smart_recruiters/models/model_when.rb +189 -0
  186. data/lib/smart_recruiters/models/offer_approval_webhook_payload.rb +244 -0
  187. data/lib/smart_recruiters/models/offer_properties_category.rb +208 -0
  188. data/lib/smart_recruiters/models/offer_properties_definition.rb +199 -0
  189. data/lib/smart_recruiters/models/offer_property_definition.rb +258 -0
  190. data/lib/smart_recruiters/models/offer_webhook_payload.rb +230 -0
  191. data/lib/smart_recruiters/models/onboarding_assignments_completed_webhook_payload.rb +216 -0
  192. data/lib/smart_recruiters/models/onboarding_high_priority_assignments_completed_webhook_payload.rb +216 -0
  193. data/lib/smart_recruiters/models/onboarding_process_webhook_payload.rb +216 -0
  194. data/lib/smart_recruiters/models/onboarding_status.rb +236 -0
  195. data/lib/smart_recruiters/models/one_of_candidate_property_value.rb +188 -0
  196. data/lib/smart_recruiters/models/one_of_consent_requests_results_results_items.rb +188 -0
  197. data/lib/smart_recruiters/models/one_of_json_patch_items.rb +188 -0
  198. data/lib/smart_recruiters/models/path.rb +202 -0
  199. data/lib/smart_recruiters/models/personal_details.rb +242 -0
  200. data/lib/smart_recruiters/models/position_webhook_payload.rb +216 -0
  201. data/lib/smart_recruiters/models/posting_status.rb +21 -0
  202. data/lib/smart_recruiters/models/privacy_policy.rb +206 -0
  203. data/lib/smart_recruiters/models/properties.rb +208 -0
  204. data/lib/smart_recruiters/models/property.rb +211 -0
  205. data/lib/smart_recruiters/models/publication.rb +244 -0
  206. data/lib/smart_recruiters/models/publication_list.rb +204 -0
  207. data/lib/smart_recruiters/models/publication_list_item.rb +239 -0
  208. data/lib/smart_recruiters/models/relation_job_property_value.rb +211 -0
  209. data/lib/smart_recruiters/models/review_webhook_payload.rb +216 -0
  210. data/lib/smart_recruiters/models/salary_range.rb +215 -0
  211. data/lib/smart_recruiters/models/screening_answer.rb +313 -0
  212. data/lib/smart_recruiters/models/screening_answers.rb +218 -0
  213. data/lib/smart_recruiters/models/screening_question.rb +246 -0
  214. data/lib/smart_recruiters/models/screening_question_answer.rb +218 -0
  215. data/lib/smart_recruiters/models/screening_question_field.rb +315 -0
  216. data/lib/smart_recruiters/models/screening_questions_answers_webhook_payload.rb +230 -0
  217. data/lib/smart_recruiters/models/secret_key_payload.rb +197 -0
  218. data/lib/smart_recruiters/models/source.rb +244 -0
  219. data/lib/smart_recruiters/models/source_actions.rb +202 -0
  220. data/lib/smart_recruiters/models/source_definition.rb +230 -0
  221. data/lib/smart_recruiters/models/source_details.rb +225 -0
  222. data/lib/smart_recruiters/models/source_types.rb +218 -0
  223. data/lib/smart_recruiters/models/source_types_actions.rb +202 -0
  224. data/lib/smart_recruiters/models/source_types_content.rb +246 -0
  225. data/lib/smart_recruiters/models/sources.rb +246 -0
  226. data/lib/smart_recruiters/models/subscription.rb +270 -0
  227. data/lib/smart_recruiters/models/subscription_request.rb +236 -0
  228. data/lib/smart_recruiters/models/subscriptions.rb +192 -0
  229. data/lib/smart_recruiters/models/user_identity.rb +215 -0
  230. data/lib/smart_recruiters/models/uuid.rb +188 -0
  231. data/lib/smart_recruiters/models/valid_job_status.rb +24 -0
  232. data/lib/smart_recruiters/models/web.rb +233 -0
  233. data/lib/smart_recruiters/models/web_profile.rb +233 -0
  234. data/lib/smart_recruiters/version.rb +5 -0
  235. data/lib/smart_recruiters.rb +46 -0
  236. data/open_api_smart_recruiters_sdk.gemspec +45 -0
  237. data/sig/smart_recruiters.rbs +4 -0
  238. metadata +367 -0
@@ -0,0 +1,379 @@
1
+ require 'date'
2
+ require 'json'
3
+ require 'logger'
4
+ require 'tempfile'
5
+ require 'typhoeus'
6
+ require 'uri'
7
+
8
+ module SmartRecruiters
9
+ class ApiClient
10
+ # The Configuration object holding settings to be used in the API client.
11
+ attr_accessor :config
12
+
13
+ # Defines the headers to be used in HTTP requests of all API calls by default.
14
+ #
15
+ # @return [Hash]
16
+ attr_accessor :default_headers
17
+
18
+ # Initializes the ApiClient
19
+ # @option config [Configuration] Configuration for initializing the object, default to Configuration.default
20
+ def initialize(access_token, config = Configuration.default)
21
+ @config = config
22
+ @config.access_token = access_token
23
+ @user_agent = "SmartRecruiters/#{VERSION}/ruby"
24
+ @default_headers = {
25
+ 'Content-Type' => 'application/json',
26
+ 'User-Agent' => @user_agent
27
+ }
28
+ end
29
+
30
+ def self.default
31
+ @@default ||= ApiClient.new
32
+ end
33
+
34
+ # Call an API with given options.
35
+ #
36
+ # @return [Array<(Object, Integer, Hash)>] an array of 3 elements:
37
+ # the data deserialized from response body (could be nil), response status code and response headers.
38
+ def call_api(http_method, path, opts = {})
39
+ request = build_request(http_method, path, opts)
40
+ response = request.run
41
+
42
+ if @config.debugging
43
+ @config.logger.debug "HTTP response body ~BEGIN~\n#{response.body}\n~END~\n"
44
+ end
45
+
46
+ unless response.success?
47
+ if response.timed_out?
48
+ fail ApiError.new('Connection timed out')
49
+ elsif response.code == 0
50
+ # Errors from libcurl will be made visible here
51
+ fail ApiError.new(:code => 0,
52
+ :message => response.return_message)
53
+ else
54
+ # Need to add logic to automatically refresh token if it is expired
55
+ fail ApiError.new(:code => response.code,
56
+ :response_headers => response.headers,
57
+ :response_body => response.body),
58
+ response.status_message
59
+ end
60
+ end
61
+
62
+ if opts[:return_type]
63
+ data = deserialize(response, opts[:return_type])
64
+ else
65
+ data = nil
66
+ end
67
+ return data, response.code, response.headers
68
+ end
69
+
70
+ # Builds the HTTP request
71
+ #
72
+ # @param [String] http_method HTTP method/verb (e.g. POST)
73
+ # @param [String] path URL path (e.g. /account/new)
74
+ # @option opts [Hash] :header_params Header parameters
75
+ # @option opts [Hash] :query_params Query parameters
76
+ # @option opts [Hash] :form_params Query parameters
77
+ # @option opts [Object] :body HTTP body (JSON/XML)
78
+ # @return [Typhoeus::Request] A Typhoeus Request
79
+ def build_request(http_method, path, opts = {})
80
+ url = build_request_url(path)
81
+ http_method = http_method.to_sym.downcase
82
+
83
+ header_params = @default_headers.merge(opts[:header_params] || {})
84
+ query_params = opts[:query_params] || {}
85
+ form_params = opts[:form_params] || {}
86
+
87
+ update_params_for_auth! header_params, query_params, opts[:auth_names]
88
+
89
+ # set ssl_verifyhosts option based on @config.verify_ssl_host (true/false)
90
+ _verify_ssl_host = @config.verify_ssl_host ? 2 : 0
91
+
92
+ req_opts = {
93
+ :method => http_method,
94
+ :headers => header_params,
95
+ :params => query_params,
96
+ :params_encoding => @config.params_encoding,
97
+ :timeout => @config.timeout,
98
+ :ssl_verifypeer => @config.verify_ssl,
99
+ :ssl_verifyhost => _verify_ssl_host,
100
+ :sslcert => @config.cert_file,
101
+ :sslkey => @config.key_file,
102
+ :verbose => @config.debugging
103
+ }
104
+
105
+ # set custom cert, if provided
106
+ req_opts[:cainfo] = @config.ssl_ca_cert if @config.ssl_ca_cert
107
+
108
+ if [:post, :patch, :put, :delete].include?(http_method)
109
+ req_body = build_request_body(header_params, form_params, opts[:body])
110
+ req_opts.update :body => req_body
111
+ if @config.debugging
112
+ @config.logger.debug "HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n"
113
+ end
114
+ end
115
+
116
+ request = Typhoeus::Request.new(url, req_opts)
117
+ download_file(request) if opts[:return_type] == 'File'
118
+ request
119
+ end
120
+
121
+ # Builds the HTTP request body
122
+ #
123
+ # @param [Hash] header_params Header parameters
124
+ # @param [Hash] form_params Query parameters
125
+ # @param [Object] body HTTP body (JSON/XML)
126
+ # @return [String] HTTP body data in the form of string
127
+ def build_request_body(header_params, form_params, body)
128
+ # http form
129
+ if header_params['Content-Type'] == 'application/x-www-form-urlencoded' ||
130
+ header_params['Content-Type'] == 'multipart/form-data'
131
+ data = {}
132
+ form_params.each do |key, value|
133
+ case value
134
+ when ::File, ::Array, nil
135
+ # let typhoeus handle File, Array and nil parameters
136
+ data[key] = value
137
+ else
138
+ data[key] = value.to_s
139
+ end
140
+ end
141
+ elsif body
142
+ data = body.is_a?(String) ? body : body.to_json
143
+ else
144
+ data = nil
145
+ end
146
+ data
147
+ end
148
+
149
+ # Check if the given MIME is a JSON MIME.
150
+ # JSON MIME examples:
151
+ # application/json
152
+ # application/json; charset=UTF8
153
+ # APPLICATION/JSON
154
+ # */*
155
+ # @param [String] mime MIME
156
+ # @return [Boolean] True if the MIME is application/json
157
+ def json_mime?(mime)
158
+ (mime == '*/*') || !(mime =~ /Application\/.*json(?!p)(;.*)?/i).nil?
159
+ end
160
+
161
+ # Deserialize the response to the given return type.
162
+ #
163
+ # @param [Response] response HTTP response
164
+ # @param [String] return_type some examples: "User", "Array<User>", "Hash<String, Integer>"
165
+ def deserialize(response, return_type)
166
+ body = response.body
167
+
168
+ # handle file downloading - return the File instance processed in request callbacks
169
+ # note that response body is empty when the file is written in chunks in request on_body callback
170
+ return @tempfile if return_type == 'File'
171
+
172
+ return nil if body.nil? || body.empty?
173
+
174
+ # return response body directly for String return type
175
+ return body if return_type == 'String'
176
+
177
+ # ensuring a default content type
178
+ content_type = response.headers['Content-Type'] || 'application/json'
179
+
180
+ fail "Content-Type is not supported: #{content_type}" unless json_mime?(content_type)
181
+
182
+ begin
183
+ data = JSON.parse("[#{body}]", :symbolize_names => true)[0]
184
+ rescue JSON::ParserError => e
185
+ if %w(String Date DateTime).include?(return_type)
186
+ data = body
187
+ else
188
+ raise e
189
+ end
190
+ end
191
+
192
+ convert_to_type data, return_type
193
+ end
194
+
195
+ # Convert data to the given return type.
196
+ # @param [Object] data Data to be converted
197
+ # @param [String] return_type Return type
198
+ # @return [Mixed] Data in a particular type
199
+ def convert_to_type(data, return_type)
200
+ return nil if data.nil?
201
+ case return_type
202
+ when 'String'
203
+ data.to_s
204
+ when 'Integer'
205
+ data.to_i
206
+ when 'Float'
207
+ data.to_f
208
+ when 'Boolean'
209
+ data == true
210
+ when 'DateTime'
211
+ # parse date time (expecting ISO 8601 format)
212
+ DateTime.parse data
213
+ when 'Date'
214
+ # parse date time (expecting ISO 8601 format)
215
+ Date.parse data
216
+ when 'Object'
217
+ # generic object (usually a Hash), return directly
218
+ data
219
+ when /\AArray<(.+)>\z/
220
+ # e.g. Array<Pet>
221
+ sub_type = $1
222
+ data.map { |item| convert_to_type(item, sub_type) }
223
+ when /\AHash\<String, (.+)\>\z/
224
+ # e.g. Hash<String, Integer>
225
+ sub_type = $1
226
+ {}.tap do |hash|
227
+ data.each { |k, v| hash[k] = convert_to_type(v, sub_type) }
228
+ end
229
+ else
230
+ # models, e.g. Pet
231
+ SmartRecruiters.const_get(return_type).build_from_hash(data)
232
+ end
233
+ end
234
+
235
+ # Save response body into a file in (the defined) temporary folder, using the filename
236
+ # from the "Content-Disposition" header if provided, otherwise a random filename.
237
+ # The response body is written to the file in chunks in order to handle files which
238
+ # size is larger than maximum Ruby String or even larger than the maximum memory a Ruby
239
+ # process can use.
240
+ #
241
+ # @see Configuration#temp_folder_path
242
+ def download_file(request)
243
+ tempfile = nil
244
+ encoding = nil
245
+ request.on_headers do |response|
246
+ content_disposition = response.headers['Content-Disposition']
247
+ if content_disposition && content_disposition =~ /filename=/i
248
+ filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
249
+ prefix = sanitize_filename(filename)
250
+ else
251
+ prefix = 'download-'
252
+ end
253
+ prefix = prefix + '-' unless prefix.end_with?('-')
254
+ encoding = response.body.encoding
255
+ tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding)
256
+ @tempfile = tempfile
257
+ end
258
+ request.on_body do |chunk|
259
+ chunk.force_encoding(encoding)
260
+ tempfile.write(chunk)
261
+ end
262
+ request.on_complete do |response|
263
+ if tempfile
264
+ tempfile.close
265
+ @config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\
266
+ "with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
267
+ "will be deleted automatically with GC. It's also recommended to delete the temp file "\
268
+ "explicitly with `tempfile.delete`"
269
+ end
270
+ end
271
+ end
272
+
273
+ # Sanitize filename by removing path.
274
+ # e.g. ../../sun.gif becomes sun.gif
275
+ #
276
+ # @param [String] filename the filename to be sanitized
277
+ # @return [String] the sanitized filename
278
+ def sanitize_filename(filename)
279
+ filename.gsub(/.*[\/\\]/, '')
280
+ end
281
+
282
+ def build_request_url(path)
283
+ # Add leading and trailing slashes to path
284
+ path = "/#{path}".gsub(/\/+/, '/')
285
+ @config.base_url + path
286
+ end
287
+
288
+ # Update hearder and query params based on authentication settings.
289
+ #
290
+ # @param [Hash] header_params Header parameters
291
+ # @param [Hash] query_params Query parameters - disabled
292
+ # @param [String] auth_names Authentication scheme name
293
+ def update_params_for_auth!(header_params, query_params, auth_names)
294
+ Array(auth_names).each do |auth_name|
295
+ auth_setting = @config.auth_settings[auth_name]
296
+ next unless auth_setting
297
+ case auth_setting[:in]
298
+ when 'header' then header_params[auth_setting[:key]] = auth_setting[:value]
299
+ # when 'query' then query_params[auth_setting[:key]] = auth_setting[:value]
300
+ else fail ArgumentError, 'Authentication token must be in `query` of `header`'
301
+ end
302
+ end
303
+ end
304
+
305
+ # Sets user agent in HTTP header
306
+ #
307
+ # @param [String] user_agent User agent (e.g. smart_recruiters/ruby/1.0.0)
308
+ def user_agent=(user_agent)
309
+ @user_agent = user_agent
310
+ @default_headers['User-Agent'] = @user_agent
311
+ end
312
+
313
+ # Return Accept header based on an array of accepts provided.
314
+ # @param [Array] accepts array for Accept
315
+ # @return [String] the Accept header (e.g. application/json)
316
+ def select_header_accept(accepts)
317
+ return nil if accepts.nil? || accepts.empty?
318
+ # use JSON when present, otherwise use all of the provided
319
+ json_accept = accepts.find { |s| json_mime?(s) }
320
+ json_accept || accepts.join(',')
321
+ end
322
+
323
+ # Return Content-Type header based on an array of content types provided.
324
+ # @param [Array] content_types array for Content-Type
325
+ # @return [String] the Content-Type header (e.g. application/json)
326
+ def select_header_content_type(content_types)
327
+ # use application/json by default
328
+ return 'application/json' if content_types.nil? || content_types.empty?
329
+ # use JSON when present, otherwise use the first one
330
+ json_content_type = content_types.find { |s| json_mime?(s) }
331
+ json_content_type || content_types.first
332
+ end
333
+
334
+ # Convert object (array, hash, object, etc) to JSON string.
335
+ # @param [Object] model object to be converted into JSON string
336
+ # @return [String] JSON string representation of the object
337
+ def object_to_http_body(model)
338
+ return model if model.nil? || model.is_a?(String)
339
+ local_body = nil
340
+ if model.is_a?(Array)
341
+ local_body = model.map { |m| object_to_hash(m) }
342
+ else
343
+ local_body = object_to_hash(model)
344
+ end
345
+ local_body.to_json
346
+ end
347
+
348
+ # Convert object(non-array) to hash.
349
+ # @param [Object] obj object to be converted into JSON string
350
+ # @return [String] JSON string representation of the object
351
+ def object_to_hash(obj)
352
+ if obj.respond_to?(:to_hash)
353
+ obj.to_hash
354
+ else
355
+ obj
356
+ end
357
+ end
358
+
359
+ # Build parameter value according to the given collection format.
360
+ # @param [String] collection_format one of :csv, :ssv, :tsv, :pipes and :multi
361
+ def build_collection_param(param, collection_format)
362
+ case collection_format
363
+ when :csv
364
+ param.join(',')
365
+ when :ssv
366
+ param.join(' ')
367
+ when :tsv
368
+ param.join("\t")
369
+ when :pipes
370
+ param.join('|')
371
+ when :multi
372
+ # return the array directly as typhoeus will handle it as expected
373
+ param
374
+ else
375
+ fail "unknown collection format: #{collection_format.inspect}"
376
+ end
377
+ end
378
+ end
379
+ end
@@ -0,0 +1,46 @@
1
+ module SmartRecruiters
2
+ class ApiError < StandardError
3
+ attr_reader :code, :response_headers, :response_body
4
+
5
+ # Usage examples:
6
+ # ApiError.new
7
+ # ApiError.new("message")
8
+ # ApiError.new(:code => 500, :response_headers => {}, :response_body => "")
9
+ # ApiError.new(:code => 404, :message => "Not Found")
10
+ def initialize(arg = nil)
11
+ if arg.is_a? Hash
12
+ if arg.key?(:message) || arg.key?('message')
13
+ super(arg[:message] || arg['message'])
14
+ else
15
+ super arg
16
+ end
17
+
18
+ arg.each do |k, v|
19
+ instance_variable_set "@#{k}", v
20
+ end
21
+ else
22
+ super arg
23
+ end
24
+ end
25
+
26
+ # Override to_s to display a friendly error message
27
+ def to_s
28
+ message
29
+ end
30
+
31
+ def message
32
+ if @message.nil?
33
+ msg = "Error message: the server returns an error"
34
+ else
35
+ msg = @message
36
+ end
37
+
38
+ msg += "\nHTTP status code: #{code}" if code
39
+ msg += "\nResponse headers: #{response_headers}" if response_headers
40
+ msg += "\nResponse body: #{response_body}" if response_body
41
+
42
+ msg
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,66 @@
1
+ require 'date'
2
+ require 'json'
3
+ require 'logger'
4
+ require 'tempfile'
5
+ require 'typhoeus'
6
+ require 'uri'
7
+
8
+ module SmartRecruiters
9
+ class Authorization
10
+ # The Configuration object holding settings to be used in the API client.
11
+ attr_accessor :config
12
+
13
+ attr_accessor :oauth_client
14
+
15
+ # Initializes the Authorization
16
+ # @option config [Configuration] Configuration for initializing the object, default to Configuration.default
17
+ def initialize(config = Configuration.default)
18
+ @config = config
19
+ @oauth_client = OAuth2::Client.new(
20
+ config.client_id,
21
+ config.client_secret,
22
+ authorize_url: 'https://www.smartrecruiters.com/identity/oauth/allow',
23
+ token_url: 'https://api.smartrecruiters.com/identity/oauth/token',
24
+ redirect_uri: config.redirect_uri,
25
+ params: { client_id: config.client_id, client_secret: config.client_secret }
26
+ )
27
+ end
28
+
29
+ def self.default
30
+ @@default ||= Authorization.new
31
+ end
32
+
33
+ def build_authorize_url
34
+ authorize_url = client.auth_code.authorize_url
35
+
36
+ unless @config.scopes.length.zero?
37
+ authorize_url += "&scope=" + @config.scopes.join("%20")
38
+ end
39
+
40
+ authorize_url
41
+ end
42
+
43
+ def get_token(code)
44
+ oauth_client.auth_code.get_token(
45
+ code,
46
+ { client_id: config.client_id, client_secret: config.client_secret }
47
+ )
48
+ end
49
+
50
+ def refresh_token
51
+ token = OAuth2::AccessToken.from_hash(
52
+ oauth_client,
53
+ {
54
+ "token_type": "Bearer",
55
+ expires_at: config.token_expires_at,
56
+ access_token: config.access_token,
57
+ refresh_token: config.refresh_token
58
+ }
59
+ )
60
+
61
+ token.refresh!({ client_id: config.client_id, client_secret: config.client_secret }) if token.expired?
62
+
63
+ token
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,214 @@
1
+ module SmartRecruiters
2
+ class Configuration
3
+ # Defines url scheme
4
+ attr_accessor :scheme
5
+
6
+ # Defines url host
7
+ attr_accessor :host
8
+
9
+ # Defines url base path
10
+ attr_accessor :base_path
11
+
12
+ # Defines API keys used with API Key authentications.
13
+ #
14
+ # @return [Hash] key: parameter name, value: parameter value (API key)
15
+ #
16
+ # @example parameter name is "api_key", API key is "xxx" (e.g. "api_key=xxx" in query string)
17
+ # config.api_key['api_key'] = 'xxx'
18
+ attr_accessor :api_key
19
+
20
+ # Defines API key prefixes used with API Key authentications.
21
+ #
22
+ # @return [Hash] key: parameter name, value: API key prefix
23
+ #
24
+ # @example parameter name is "Authorization", API key prefix is "Token" (e.g. "Authorization: Token xxx" in headers)
25
+ # config.api_key_prefix['api_key'] = 'Token'
26
+ attr_accessor :api_key_prefix
27
+
28
+ # Defines the application client_id.
29
+ #
30
+ # @return [String]
31
+ attr_accessor :client_id
32
+
33
+ # Defines the application client_secret.
34
+ #
35
+ # @return [String]
36
+ attr_accessor :client_secret
37
+
38
+ # Defines the scopes used with OAuth2 authentication.
39
+ #
40
+ # @return [Array<(String)>]
41
+ attr_accessor :scopes
42
+
43
+ # Defines the redirect uri for OAuth2 callback.
44
+ #
45
+ # @return [String]
46
+ attr_accessor :redirect_uri
47
+
48
+ # Defines the access token (Bearer) used with OAuth2.
49
+ attr_accessor :access_token
50
+
51
+ # Defines the refresh token used with OAuth2.
52
+ attr_accessor :refresh_token
53
+
54
+ # Defines the token_expires_at for the access token (Bearer) used with OAuth2.
55
+ attr_accessor :token_expires_at
56
+
57
+ # Set this to enable/disable debugging. When enabled (set to true), HTTP request/response
58
+ # details will be logged with `logger.debug` (see the `logger` attribute).
59
+ # Default to false.
60
+ #
61
+ # @return [true, false]
62
+ attr_accessor :debugging
63
+
64
+ # Defines the logger used for debugging.
65
+ # Default to `Rails.logger` (when in Rails) or logging to STDOUT.
66
+ #
67
+ # @return [#debug]
68
+ attr_accessor :logger
69
+
70
+ # Defines the temporary folder to store downloaded files
71
+ # (for API endpoints that have file response).
72
+ # Default to use `Tempfile`.
73
+ #
74
+ # @return [String]
75
+ attr_accessor :temp_folder_path
76
+
77
+ # The time limit for HTTP request in seconds.
78
+ # Default to 0 (never times out).
79
+ attr_accessor :timeout
80
+
81
+ # Set this to false to skip client side validation in the operation.
82
+ # Default to true.
83
+ # @return [true, false]
84
+ attr_accessor :client_side_validation
85
+
86
+ ### TLS/SSL setting
87
+ # Set this to false to skip verifying SSL certificate when calling API from https server.
88
+ # Default to true.
89
+ #
90
+ # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks.
91
+ #
92
+ # @return [true, false]
93
+ attr_accessor :verify_ssl
94
+
95
+ ### TLS/SSL setting
96
+ # Set this to false to skip verifying SSL host name
97
+ # Default to true.
98
+ #
99
+ # @note Do NOT set it to false in production code, otherwise you would face multiple types of cryptographic attacks.
100
+ #
101
+ # @return [true, false]
102
+ attr_accessor :verify_ssl_host
103
+
104
+ ### TLS/SSL setting
105
+ # Set this to customize the certificate file to verify the peer.
106
+ #
107
+ # @return [String] the path to the certificate file
108
+ #
109
+ # @see The `cainfo` option of Typhoeus, `--cert` option of libcurl. Related source code:
110
+ # https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/easy_factory.rb#L145
111
+ attr_accessor :ssl_ca_cert
112
+
113
+ ### TLS/SSL setting
114
+ # Client certificate file (for client certificate)
115
+ attr_accessor :cert_file
116
+
117
+ ### TLS/SSL setting
118
+ # Client private key file (for client certificate)
119
+ attr_accessor :key_file
120
+
121
+ # Set this to customize parameters encoding of array parameter with multi collectionFormat.
122
+ # Default to nil.
123
+ #
124
+ # @see The params_encoding option of Ethon. Related source code:
125
+ # https://github.com/typhoeus/ethon/blob/master/lib/ethon/easy/queryable.rb#L96
126
+ attr_accessor :params_encoding
127
+
128
+ attr_accessor :inject_format
129
+
130
+ attr_accessor :force_ending_format
131
+
132
+ def initialize
133
+ @scheme = 'https'
134
+ @host = 'api.smartrecruiters.com'
135
+ @base_path = 'https://api.smartrecruiters.com'
136
+ @api_key = {}
137
+ @api_key_prefix = {}
138
+ @client_id = nil
139
+ @client_secret = nil
140
+ @scopes = []
141
+ @redirect_uri = nil
142
+ @timeout = 0
143
+ @client_side_validation = true
144
+ @verify_ssl = true
145
+ @verify_ssl_host = true
146
+ @params_encoding = nil
147
+ @cert_file = nil
148
+ @key_file = nil
149
+ @debugging = false
150
+ @inject_format = false
151
+ @force_ending_format = false
152
+ @logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
153
+
154
+ yield(self) if block_given?
155
+ end
156
+
157
+ # The default Configuration object.
158
+ def self.default
159
+ @@default ||= Configuration.new
160
+ end
161
+
162
+ def configure
163
+ yield(self) if block_given?
164
+ end
165
+
166
+ def scheme=(scheme)
167
+ # remove :// from scheme
168
+ @scheme = scheme.sub(/:\/\//, '')
169
+ end
170
+
171
+ def host=(host)
172
+ # remove http(s):// and anything after a slash
173
+ @host = host.sub(/https?:\/\//, '').split('/').first
174
+ end
175
+
176
+ def base_path=(base_path)
177
+ # Add leading and trailing slashes to base_path
178
+ @base_path = "/#{base_path}".gsub(/\/+/, '/')
179
+ @base_path = '' if @base_path == '/'
180
+ end
181
+
182
+ def base_url
183
+ "#{scheme}://#{[host, base_path].join('/').gsub(/\/+/, '/')}".sub(/\/+\z/, '')
184
+ end
185
+
186
+ # Gets API key (with prefix if set).
187
+ # @param [String] param_name the parameter name of API key auth
188
+ def api_key_with_prefix(param_name)
189
+ if @api_key_prefix[param_name]
190
+ "#{@api_key_prefix[param_name]} #{@api_key[param_name]}"
191
+ else
192
+ @api_key[param_name]
193
+ end
194
+ end
195
+
196
+ # Gets Basic Auth token string
197
+ def basic_auth_token
198
+ 'Basic ' + ["#{username}:#{password}"].pack('m').delete("\r\n")
199
+ end
200
+
201
+ # Returns Auth Settings hash for api client.
202
+ def auth_settings
203
+ {
204
+ 'key' =>
205
+ {
206
+ type: 'oauth',
207
+ in: 'header',
208
+ key: 'Authorization',
209
+ value: "Bearer #{access_token}"
210
+ },
211
+ }
212
+ end
213
+ end
214
+ end