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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/lib/carehq/exceptions.rb +79 -0
  3. data/lib/carehq.rb +140 -0
  4. 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: []