messagemedia_webhooks_sdk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +28 -0
  3. data/README.md +132 -0
  4. data/lib/message_media_webhooks.rb +39 -0
  5. data/lib/message_media_webhooks/api_helper.rb +272 -0
  6. data/lib/message_media_webhooks/configuration.rb +28 -0
  7. data/lib/message_media_webhooks/controllers/base_controller.rb +55 -0
  8. data/lib/message_media_webhooks/controllers/webhooks_controller.rb +369 -0
  9. data/lib/message_media_webhooks/exceptions/api_exception.rb +16 -0
  10. data/lib/message_media_webhooks/exceptions/create_webhook_400_response_exception.rb +25 -0
  11. data/lib/message_media_webhooks/exceptions/retrieve_webhook_400_response_exception.rb +26 -0
  12. data/lib/message_media_webhooks/exceptions/update_webhook_400_response_exception.rb +25 -0
  13. data/lib/message_media_webhooks/http/auth/basic_auth.rb +20 -0
  14. data/lib/message_media_webhooks/http/faraday_client.rb +54 -0
  15. data/lib/message_media_webhooks/http/http_call_back.rb +20 -0
  16. data/lib/message_media_webhooks/http/http_client.rb +90 -0
  17. data/lib/message_media_webhooks/http/http_context.rb +16 -0
  18. data/lib/message_media_webhooks/http/http_method_enum.rb +9 -0
  19. data/lib/message_media_webhooks/http/http_request.rb +46 -0
  20. data/lib/message_media_webhooks/http/http_response.rb +19 -0
  21. data/lib/message_media_webhooks/message_media_webhooks_client.rb +26 -0
  22. data/lib/message_media_webhooks/models/base_model.rb +32 -0
  23. data/lib/message_media_webhooks/models/create_webhook_request.rb +76 -0
  24. data/lib/message_media_webhooks/models/update_webhook_request.rb +67 -0
  25. data/test/controllers/controller_test_base.rb +30 -0
  26. data/test/controllers/test_webhooks_controller.rb +56 -0
  27. data/test/http_response_catcher.rb +16 -0
  28. data/test/test_helper.rb +91 -0
  29. metadata +155 -0
@@ -0,0 +1,28 @@
1
+
2
+
3
+ module MessageMediaWebhooks
4
+
5
+ Logging.logger.root.appenders = Logging.appenders.stdout
6
+ Logging.logger.root.level = :info
7
+
8
+ # All configuration including auth info and base URI for the API access
9
+ # are configured in this class.
10
+ class Configuration
11
+ # The base Uri for API calls
12
+ @base_uri = 'https://api.messagemedia.com'
13
+
14
+ # The username to use with basic authentication
15
+ @basic_auth_user_name = 'TODO: Replace'
16
+
17
+ # The password to use with basic authentication
18
+ @basic_auth_password = 'TODO: Replace'
19
+
20
+ # The attribute accessors for public properties.
21
+ class << self
22
+ attr_accessor :array_serialization
23
+ attr_accessor :base_uri
24
+ attr_accessor :basic_auth_user_name
25
+ attr_accessor :basic_auth_password
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,55 @@
1
+
2
+ module MessageMediaWebhooks
3
+ # Base controller.
4
+ class BaseController
5
+ attr_accessor :http_client, :http_call_back
6
+
7
+ def initialize(http_client: nil, http_call_back: nil)
8
+ @http_client = http_client || FaradayClient.new
9
+ @http_call_back = http_call_back
10
+
11
+ @global_headers = {
12
+ 'user-agent' => 'messagemedia-webhooks-ruby-sdk-1.0.0'
13
+ }
14
+ @logger = Logging.logger[self]
15
+ @logger.info("Instantiated controller class.")
16
+ end
17
+
18
+ def validate_parameters(args)
19
+ args.each do |_name, value|
20
+ if value.nil?
21
+ raise ArgumentError, "Required parameter #{_name} cannot be nil."
22
+ end
23
+ end
24
+ end
25
+
26
+ def execute_request(request, binary: false, name: nil)
27
+ @logger.info("Calling the on_before_request method of http_call_back for #{name}.") if @http_call_back
28
+ @http_call_back.on_before_request(request) if @http_call_back
29
+
30
+ @logger.info("Merging global headers with endpoint headers for #{name}.")
31
+ APIHelper.clean_hash(request.headers)
32
+ request.headers = @global_headers.clone.merge(request.headers)
33
+
34
+ @logger.debug("Raw request for #{name} is: #{request.inspect}")
35
+ response = if binary
36
+ @http_client.execute_as_binary(request)
37
+ else
38
+ @http_client.execute_as_string(request)
39
+ end
40
+ @logger.debug("Raw response for #{name} is: #{response.inspect}")
41
+ @logger.info("Wrapping request and response in a context object for #{name}.")
42
+ context = HttpContext.new(request, response)
43
+
44
+ @logger.info("Calling on_after_response method of http_call_back for #{name}.") if @http_call_back
45
+ @http_call_back.on_after_response(context) if @http_call_back
46
+
47
+ context
48
+ end
49
+
50
+ def validate_response(context)
51
+ raise APIException.new 'HTTP Response Not OK', context unless
52
+ context.response.status_code.between?(200, 208) # [200,208] = HTTP OK
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,369 @@
1
+
2
+ module MessageMediaWebhooks
3
+ # WebhooksController
4
+ class WebhooksController < BaseController
5
+ @instance = WebhooksController.new
6
+
7
+ class << self
8
+ attr_accessor :instance
9
+ end
10
+
11
+ def instance
12
+ self.class.instance
13
+ end
14
+
15
+ # Create a webhook for one or more of the specified events.
16
+ # A webhook would typically have the following structure:
17
+ # ```
18
+ # {
19
+ # "url": "http://webhook.com",
20
+ # "method": "POST",
21
+ # "encoding": "JSON",
22
+ # "headers": {
23
+ # "Account": "DeveloperPortal7000"
24
+ # },
25
+ # "events": [
26
+ # "RECEIVED_SMS"
27
+ # ],
28
+ # "template": "{\"id\":\"$mtId\",\"status\":\"$statusCode\"}"
29
+ # }
30
+ # ```
31
+ # A valid webhook must consist of the following properties:
32
+ # - ```url``` The configured URL which will trigger the webhook when a
33
+ # selected event occurs.
34
+ # - ```method``` The methods to map CRUD (create, retrieve, update, delete)
35
+ # operations to HTTP requests.
36
+ # - ```encoding``` The format in which the payload will be returned. You can
37
+ # choose from ```JSON```, ```FORM_ENCODED``` or ```XML```. This will
38
+ # automatically add the Content-Type header for you so you don't have to add
39
+ # it again in the `headers` property.
40
+ # - ```headers``` HTTP header fields which provide required information
41
+ # about the request or response, or about the object sent in the message
42
+ # body. This should not include the `Content-Type` header.
43
+ # - ```events``` Event or events that will trigger the webhook. Atleast one
44
+ # event should be present.
45
+ # - ```template``` The structure of the payload that will be returned.
46
+ # #### Types of Events
47
+ # You can select all of the events (listed below) or combine them in
48
+ # whatever way you like but atleast one event must be used. Otherwise, the
49
+ # webhook won't be created.
50
+ # A webhook will be triggered when any one or more of the events occur:
51
+ # + **SMS**
52
+ # + `RECEIVED_SMS` Receive an SMS
53
+ # + `OPT_OUT_SMS` Opt-out occured
54
+ # + **MMS**
55
+ # + `RECEIVED_MMS` Receive an MMS
56
+ # + **DR (Delivery Reports)**
57
+ # + `ENROUTE_DR` Message is enroute
58
+ # + `EXPIRED_DR` Message has expired
59
+ # + `REJECTED_DR` Message is rejected
60
+ # + `FAILED_DR` Message has failed
61
+ # + `DELIVERED_DR` Message is delivered
62
+ # + `SUBMITTED_DR` Message is submitted
63
+ # #### Template Parameters
64
+ # You can choose what to include in the data that will be sent as the
65
+ # payload via the Webhook.
66
+ # Keep in my mind, you must escape the JSON in the template value (see
67
+ # example above).
68
+ # The table illustrates a list of all the parameters that can be included in
69
+ # the template and which event types it can be applied to.
70
+ # | Data | Parameter Name | Example | Event Type |
71
+ # |:--|--|--|--|--|
72
+ # | **Service Type** | $type| `SMS` | `DR` `MO` `MO MMS` |
73
+ # | **Message ID** | $mtId, $messageId|
74
+ # `877c19ef-fa2e-4cec-827a-e1df9b5509f7` | `DR` `MO` `MO MMS`|
75
+ # | **Delivery Report ID** |$drId, $reportId|
76
+ # `01e1fa0a-6e27-4945-9cdb-18644b4de043` | `DR` |
77
+ # | **Reply ID**| $moId, $replyId| `a175e797-2b54-468b-9850-41a3eab32f74` |
78
+ # `MO` `MO MMS` |
79
+ # | **Account ID** | $accountId| `DeveloperPortal7000` | `DR` `MO` `MO MMS`
80
+ # |
81
+ # | **Message Timestamp** | $submittedTimestamp| `2016-12-07T08:43:00.850Z`
82
+ # | `DR` `MO` `MO MMS` |
83
+ # | **Provider Timestamp** | $receivedTimestamp| `2016-12-07T08:44:00.850Z`
84
+ # | `DR` `MO` `MO MMS` |
85
+ # | **Message Status** | $status| `enroute` | `DR` |
86
+ # | **Status Code** | $statusCode| `200` | `DR` |
87
+ # | **External Metadata** | $metadata.get('key')| `name` | `DR` `MO` `MO
88
+ # MMS` |
89
+ # | **Source Address**| $sourceAddress| `+61491570156` | `DR` `MO` `MO MMS`
90
+ # |
91
+ # | **Destination Address**| $destinationAddress| `+61491593156` | `MO` `MO
92
+ # MMS` |
93
+ # | **Message Content**| $mtContent, $messageContent| `Hi Derp` | `DR` `MO`
94
+ # `MO MMS` |
95
+ # | **Reply Content**| $moContent, $replyContent| `Hello Derpina` | `MO` `MO
96
+ # MMS` |
97
+ # | **Retry Count**| $retryCount| `1` | `DR` `MO` `MO MMS` |
98
+ # *Note: A 400 response will be returned if the `url` is invalid, the
99
+ # `events`, `encoding` or `method` is null or the `headers` has a
100
+ # Content-Type attribute.*
101
+ # @param [CreateWebhookRequest] body Required parameter: Example:
102
+ # @return Mixed response from the API call
103
+ def create_webhook(body)
104
+ begin
105
+ @logger.info("create_webhook called.")
106
+ # Prepare query url.
107
+ @logger.info("Preparing query URL for create_webhook.")
108
+ _query_builder = Configuration.base_uri.dup
109
+ _query_builder << '/v1/webhooks/messages'
110
+ _query_url = APIHelper.clean_url _query_builder
111
+
112
+ # Prepare headers.
113
+ @logger.info("Preparing headers for create_webhook.")
114
+ _headers = {
115
+ 'accept' => 'application/json',
116
+ 'content-type' => 'application/json; charset=utf-8'
117
+ }
118
+
119
+ # Prepare and execute HttpRequest.
120
+ @logger.info('Preparing and executing HttpRequest for create_webhook.')
121
+ _request = @http_client.post(
122
+ _query_url,
123
+ headers: _headers,
124
+ parameters: body.to_json
125
+ )
126
+ BasicAuth.apply(_request)
127
+ _context = execute_request(_request, name: 'create_webhook')
128
+
129
+ # Validate response against endpoint and global error codes.
130
+ @logger.info("Validating response for create_webhook.")
131
+ if _context.response.status_code == 400
132
+ raise CreateWebhook400ResponseException.new(
133
+ 'Unexpected error in API call. See HTTP response body for details.',
134
+ _context
135
+ )
136
+ elsif _context.response.status_code == 409
137
+ raise CreateWebhook400ResponseException.new(
138
+ 'Unexpected error in API call. See HTTP response body for details.',
139
+ _context
140
+ )
141
+ end
142
+ validate_response(_context)
143
+
144
+ # Return appropriate response type.
145
+ @logger.info("Returning response for create_webhook.")
146
+ decoded = APIHelper.json_deserialize(_context.response.raw_body) unless
147
+ _context.response.raw_body.nil? ||
148
+ _context.response.raw_body.to_s.strip.empty?
149
+ decoded
150
+
151
+ rescue Exception => e
152
+ @logger.error(e)
153
+ raise e
154
+ end
155
+ end
156
+
157
+ # Retrieve all the webhooks created for the connected account.
158
+ # A successful request to the retrieve webhook endpoint will return a
159
+ # response body as follows:
160
+ # ```
161
+ # {
162
+ # "page": 0,
163
+ # "pageSize": 100,
164
+ # "pageData": [
165
+ # {
166
+ # "url": "https://webhook.com",
167
+ # "method": "POST",
168
+ # "id": "8805c9d8-bef7-41c7-906a-69ede93aa024",
169
+ # "encoding": "JSON",
170
+ # "events": [
171
+ # "RECEIVED_SMS"
172
+ # ],
173
+ # "headers": {},
174
+ # "template": "{\"id\":\"$mtId\", \"status\":\"$statusCode\"}"
175
+ # }
176
+ # ]
177
+ # }
178
+ # ```
179
+ # *Note: Response 400 is returned when the `page` query parameter is not
180
+ # valid or the `pageSize` query parameter is not valid.*
181
+ # @param [Integer] page Optional parameter: Example:
182
+ # @param [Integer] page_size Optional parameter: Example:
183
+ # @return Mixed response from the API call
184
+ def retrieve_webhook(page = nil,
185
+ page_size = nil)
186
+ begin
187
+ @logger.info("retrieve_webhook called.")
188
+ # Prepare query url.
189
+ @logger.info("Preparing query URL for retrieve_webhook.")
190
+ _query_builder = Configuration.base_uri.dup
191
+ _query_builder << '/v1/webhooks/messages/'
192
+ _query_builder = APIHelper.append_url_with_query_parameters(
193
+ _query_builder,
194
+ {
195
+ 'page' => page,
196
+ 'pageSize' => page_size
197
+ },
198
+ array_serialization: Configuration.array_serialization
199
+ )
200
+ _query_url = APIHelper.clean_url _query_builder
201
+
202
+ # Prepare headers.
203
+ @logger.info("Preparing headers for retrieve_webhook.")
204
+ _headers = {
205
+ 'accept' => 'application/json'
206
+ }
207
+
208
+ # Prepare and execute HttpRequest.
209
+ @logger.info('Preparing and executing HttpRequest for retrieve_webhook.')
210
+ _request = @http_client.get(
211
+ _query_url,
212
+ headers: _headers
213
+ )
214
+ BasicAuth.apply(_request)
215
+ _context = execute_request(_request, name: 'retrieve_webhook')
216
+
217
+ # Validate response against endpoint and global error codes.
218
+ @logger.info("Validating response for retrieve_webhook.")
219
+ if _context.response.status_code == 400
220
+ raise RetrieveWebhook400ResponseException.new(
221
+ 'Unexpected error in API call. See HTTP response body for details.',
222
+ _context
223
+ )
224
+ end
225
+ validate_response(_context)
226
+
227
+ # Return appropriate response type.
228
+ @logger.info("Returning response for retrieve_webhook.")
229
+ decoded = APIHelper.json_deserialize(_context.response.raw_body) unless
230
+ _context.response.raw_body.nil? ||
231
+ _context.response.raw_body.to_s.strip.empty?
232
+ decoded
233
+
234
+ rescue Exception => e
235
+ @logger.error(e)
236
+ raise e
237
+ end
238
+ end
239
+
240
+ # Delete a webhook that was previously created for the connected account.
241
+ # A webhook can be cancelled by appending the UUID of the webhook to the
242
+ # endpoint and submitting a DELETE request to the /webhooks/messages
243
+ # endpoint.
244
+ # *Note: Only pre-created webhooks can be deleted. If an invalid or non
245
+ # existent webhook ID parameter is specified in the request, then a HTTP 404
246
+ # Not Found response will be returned.*
247
+ # @param [UUID | String] webhook_id Required parameter: Example:
248
+ # @return void response from the API call
249
+ def delete_webhook(webhook_id)
250
+ begin
251
+ @logger.info("delete_webhook called.")
252
+ # Prepare query url.
253
+ @logger.info("Preparing query URL for delete_webhook.")
254
+ _query_builder = Configuration.base_uri.dup
255
+ _query_builder << '/v1/webhooks/messages/{webhookId}'
256
+ _query_builder = APIHelper.append_url_with_template_parameters(
257
+ _query_builder,
258
+ 'webhookId' => webhook_id
259
+ )
260
+ _query_url = APIHelper.clean_url _query_builder
261
+
262
+ # Prepare and execute HttpRequest.
263
+ @logger.info('Preparing and executing HttpRequest for delete_webhook.')
264
+ _request = @http_client.delete(
265
+ _query_url
266
+ )
267
+ BasicAuth.apply(_request)
268
+ _context = execute_request(_request, name: 'delete_webhook')
269
+
270
+ # Validate response against endpoint and global error codes.
271
+ @logger.info("Validating response for delete_webhook.")
272
+ if _context.response.status_code == 404
273
+ raise APIException.new(
274
+ '',
275
+ _context
276
+ )
277
+ end
278
+ validate_response(_context)
279
+
280
+ rescue Exception => e
281
+ @logger.error(e)
282
+ raise e
283
+ end
284
+ end
285
+
286
+ # Update a webhook. You can update individual attributes or all of them by
287
+ # submitting a PATCH request to the /webhooks/messages endpoint (the same
288
+ # endpoint used above to delete a webhook)
289
+ # A successful request to the retrieve webhook endpoint will return a
290
+ # response body as follows:
291
+ # ```
292
+ # {
293
+ # "url": "https://webhook.com",
294
+ # "method": "POST",
295
+ # "id": "04442623-0961-464e-9cbc-ec50804e0413",
296
+ # "encoding": "JSON",
297
+ # "events": [
298
+ # "RECEIVED_SMS"
299
+ # ],
300
+ # "headers": {},
301
+ # "template": "{\"id\":\"$mtId\", \"status\":\"$statusCode\"}"
302
+ # }
303
+ # ```
304
+ # *Note: Only pre-created webhooks can be deleted. If an invalid or non
305
+ # existent webhook ID parameter is specified in the request, then a HTTP 404
306
+ # Not Found response will be returned.*
307
+ # @param [UUID | String] webhook_id Required parameter: Example:
308
+ # @param [UpdateWebhookRequest] body Required parameter: Example:
309
+ # @return Mixed response from the API call
310
+ def update_webhook(webhook_id,
311
+ body)
312
+ begin
313
+ @logger.info("update_webhook called.")
314
+ # Prepare query url.
315
+ @logger.info("Preparing query URL for update_webhook.")
316
+ _query_builder = Configuration.base_uri.dup
317
+ _query_builder << '/v1/webhooks/messages/{webhookId}'
318
+ _query_builder = APIHelper.append_url_with_template_parameters(
319
+ _query_builder,
320
+ 'webhookId' => webhook_id
321
+ )
322
+ _query_url = APIHelper.clean_url _query_builder
323
+
324
+ # Prepare headers.
325
+ @logger.info("Preparing headers for update_webhook.")
326
+ _headers = {
327
+ 'accept' => 'application/json',
328
+ 'content-type' => 'application/json; charset=utf-8'
329
+ }
330
+
331
+ # Prepare and execute HttpRequest.
332
+ @logger.info('Preparing and executing HttpRequest for update_webhook.')
333
+ _request = @http_client.patch(
334
+ _query_url,
335
+ headers: _headers,
336
+ parameters: body.to_json
337
+ )
338
+ BasicAuth.apply(_request)
339
+ _context = execute_request(_request, name: 'update_webhook')
340
+
341
+ # Validate response against endpoint and global error codes.
342
+ @logger.info("Validating response for update_webhook.")
343
+ if _context.response.status_code == 400
344
+ raise UpdateWebhook400ResponseException.new(
345
+ 'Unexpected error in API call. See HTTP response body for details.',
346
+ _context
347
+ )
348
+ elsif _context.response.status_code == 404
349
+ raise APIException.new(
350
+ '',
351
+ _context
352
+ )
353
+ end
354
+ validate_response(_context)
355
+
356
+ # Return appropriate response type.
357
+ @logger.info("Returning response for update_webhook.")
358
+ decoded = APIHelper.json_deserialize(_context.response.raw_body) unless
359
+ _context.response.raw_body.nil? ||
360
+ _context.response.raw_body.to_s.strip.empty?
361
+ decoded
362
+
363
+ rescue Exception => e
364
+ @logger.error(e)
365
+ raise e
366
+ end
367
+ end
368
+ end
369
+ end