carehq 0.0.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.
- checksums.yaml +7 -0
- data/lib/carehq/exceptions.rb +79 -0
- data/lib/carehq.rb +140 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d3d40aabe2a58bfa22dfd36fd4e4ae83b49e9e89bdc4e7e3031fdd217b204718
|
4
|
+
data.tar.gz: 5dc6072646b3caef0a22234634d3c8dc2c41474ad9fd5c4fa3146039077aaed3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f81342e9a3f80254ea673b38de329fcff8611335756490a5a598b7b45f5f5ca50bec23f5cd583284ae79eec2cd0d9a97dfe5264667d79259511d33bc5e96bd46
|
7
|
+
data.tar.gz: 906d32aa18a01523966b36ae8765b428d718a5479dbbc89f231b5d59cb30ab959616c53bc496888f6dbfabdc468b8f0b6ac30fb8537d9affa8f71e9c4a267e72
|
@@ -0,0 +1,79 @@
|
|
1
|
+
|
2
|
+
class APIException < StandardError
|
3
|
+
|
4
|
+
# An error occurred while processing an API the request.
|
5
|
+
|
6
|
+
attr_reader :status_code
|
7
|
+
attr_reader :hint
|
8
|
+
attr_reader :arg_errors
|
9
|
+
|
10
|
+
def initialize(status_code, hint=nil, arg_errors=nil)
|
11
|
+
|
12
|
+
# The status code associated with the error
|
13
|
+
@status_code = status_code
|
14
|
+
|
15
|
+
# A hint providing additional information as to why this error
|
16
|
+
# occurred.
|
17
|
+
@hint = hint
|
18
|
+
|
19
|
+
# A dictionary of errors relating to the arguments (parameters) sent
|
20
|
+
# to the API endpoint (e.g `{'arg_name': ['error1', ...]}`).
|
21
|
+
@arg_errors = arg_errors
|
22
|
+
|
23
|
+
super()
|
24
|
+
end
|
25
|
+
|
26
|
+
def APIException.get_class_by_status_code(error_type, default=nil)
|
27
|
+
|
28
|
+
class_map = {
|
29
|
+
400 => InvalidRequest,
|
30
|
+
401 => Unauthorized,
|
31
|
+
403 => Forbidden,
|
32
|
+
405 => Forbidden,
|
33
|
+
404 => NotFound,
|
34
|
+
429 => RateLimitExceeded
|
35
|
+
}
|
36
|
+
|
37
|
+
if class_map.has_key? error_type
|
38
|
+
return class_map[error_type]
|
39
|
+
|
40
|
+
elsif default
|
41
|
+
return default
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
return APIException
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
class Forbidden < APIException
|
52
|
+
|
53
|
+
# The request is not not allowed, most likely the HTTP method used to call
|
54
|
+
# the API endpoint is incorrect or the API key (via its associated account)
|
55
|
+
# does not have permission to call the endpoint and/or perform the action.
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
class InvalidRequest < APIException
|
60
|
+
# Not a valid request, most likely a missing or invalid parameter.
|
61
|
+
end
|
62
|
+
|
63
|
+
class NotFound < APIException
|
64
|
+
|
65
|
+
# The endpoint you are calling or the document you referenced doesn't exist.
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
class RateLimitExceeded < APIException
|
70
|
+
|
71
|
+
# You have exceeded the number of API requests allowed per second.
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
class Unauthorized < APIException
|
76
|
+
|
77
|
+
# The API credentials provided are not valid.
|
78
|
+
|
79
|
+
end
|
data/lib/carehq.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'httparty'
|
3
|
+
|
4
|
+
|
5
|
+
class HTTPartyWrapper
|
6
|
+
include HTTParty
|
7
|
+
|
8
|
+
query_string_normalizer proc {|query|
|
9
|
+
query.map do |key, value|
|
10
|
+
value.kind_of?(Array) ?
|
11
|
+
value.map {|v| "#{key}=#{v}"} : "#{key}=#{value}"
|
12
|
+
end.join('&')
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
class APIClient
|
18
|
+
|
19
|
+
# A client for the CareHQ API.
|
20
|
+
|
21
|
+
attr_reader :rate_limit
|
22
|
+
attr_reader :rate_limit_reset
|
23
|
+
attr_reader :rate_limit_remaining
|
24
|
+
|
25
|
+
def initialize(
|
26
|
+
account_id,
|
27
|
+
api_key,
|
28
|
+
api_secret,
|
29
|
+
api_base_url: 'https://api.carehq.co.uk',
|
30
|
+
timeout: nil
|
31
|
+
)
|
32
|
+
|
33
|
+
# The Id of the CareHQ account the API key relates to
|
34
|
+
@account_id = account_id
|
35
|
+
|
36
|
+
# A key used to authenticate API calls to an account
|
37
|
+
@api_key = api_key
|
38
|
+
|
39
|
+
# A secret used to generate a signature for each API request
|
40
|
+
@api_secret = api_secret
|
41
|
+
|
42
|
+
# The base URL to use when calling the API
|
43
|
+
@api_base_url = api_base_url
|
44
|
+
|
45
|
+
# The period of time before requests to the API should timeout
|
46
|
+
@timeout = timeout
|
47
|
+
|
48
|
+
# NOTE: Rate limiting information is only available after a request
|
49
|
+
# has been made.
|
50
|
+
|
51
|
+
# The maximum number of requests per second that can be made with the
|
52
|
+
# given API key.
|
53
|
+
@rate_limit = nil
|
54
|
+
|
55
|
+
# The time (seconds since epoch) when the current rate limit will
|
56
|
+
# reset.
|
57
|
+
@rate_limit_reset = nil
|
58
|
+
|
59
|
+
# The number of requests remaining within the current limit before the
|
60
|
+
# next reset.
|
61
|
+
@rate_limit_remaining = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def request(method, path, params: nil, data: nil)
|
65
|
+
# Call the API
|
66
|
+
|
67
|
+
# Filter out params/data set to `nil` and ensure all arguments are
|
68
|
+
# converted to strings.
|
69
|
+
|
70
|
+
if params
|
71
|
+
params.delete_if {|k, v| v.nil?}
|
72
|
+
end
|
73
|
+
|
74
|
+
if data
|
75
|
+
data.delete_if {|k, v| v.nil?}
|
76
|
+
end
|
77
|
+
|
78
|
+
# Build the signature
|
79
|
+
signature_data = method.downcase == 'get' ? params : data
|
80
|
+
|
81
|
+
signature_values = []
|
82
|
+
(signature_data or {}).each_pair do |key, value|
|
83
|
+
signature_values.push(key)
|
84
|
+
if value.kind_of?(Array)
|
85
|
+
signature_values.concat(value)
|
86
|
+
else
|
87
|
+
signature_values.push(value)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
signature_body = signature_values.join ''
|
92
|
+
|
93
|
+
timestamp = Time.now.to_f.to_s
|
94
|
+
|
95
|
+
signature_hash = Digest::SHA1.new
|
96
|
+
signature_hash.update timestamp
|
97
|
+
signature_hash.update signature_body
|
98
|
+
signature_hash.update @api_secret
|
99
|
+
signature = signature_hash.hexdigest
|
100
|
+
|
101
|
+
# Build the headers
|
102
|
+
headers = {
|
103
|
+
'Accept' => 'application/json',
|
104
|
+
'X-CareHQ-AccountId' => @account_id,
|
105
|
+
'X-CareHQ-APIKey' => @api_key,
|
106
|
+
'X-CareHQ-Signature' => signature,
|
107
|
+
'X-CareHQ-Timestamp' => timestamp
|
108
|
+
}
|
109
|
+
|
110
|
+
# Make the request
|
111
|
+
url = [@api_base_url, '/v1/', path].join ''
|
112
|
+
response = HTTPartyWrapper.method(method.downcase).call(
|
113
|
+
url,
|
114
|
+
{
|
115
|
+
:query => params,
|
116
|
+
:headers => headers,
|
117
|
+
:body => data,
|
118
|
+
:timeout => @timeout
|
119
|
+
}
|
120
|
+
)
|
121
|
+
|
122
|
+
# Raise an error related to the response
|
123
|
+
|
124
|
+
# Handle a successful response
|
125
|
+
if [200, 204].include? response.code
|
126
|
+
return response
|
127
|
+
end
|
128
|
+
|
129
|
+
error_cls = APIException.get_class_by_status_code(response.code)
|
130
|
+
raise error_cls.new(
|
131
|
+
response.code,
|
132
|
+
response['hint'],
|
133
|
+
response['arg_errors']
|
134
|
+
)
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
require 'carehq/exceptions'
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: carehq
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Anthony Blackshaw
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-09-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.20.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.20.0
|
27
|
+
description: CareHQ API client for Ruby.
|
28
|
+
email: ant@crmhq.co.uk
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/carehq.rb
|
34
|
+
- lib/carehq/exceptions.rb
|
35
|
+
homepage: https://github.com/CareHQ/carehq-ruby
|
36
|
+
licenses:
|
37
|
+
- MIT
|
38
|
+
metadata: {}
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 2.7.6
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: CareHQ API client
|
59
|
+
test_files: []
|