reach-ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +1 -0
  3. data/.gitignore +18 -0
  4. data/.rubocop.yml +58 -0
  5. data/.rubocop_todo.yml +193 -0
  6. data/AUTHORS.md +52 -0
  7. data/CHANGES.md +3 -0
  8. data/CODE_OF_CONDUCT.md +73 -0
  9. data/CONTRIBUTING.md +163 -0
  10. data/Dockerfile +9 -0
  11. data/Gemfile +3 -0
  12. data/ISSUE_TEMPLATE.md +30 -0
  13. data/LICENSE +21 -0
  14. data/Makefile +34 -0
  15. data/PULL_REQUEST_TEMPLATE.md +31 -0
  16. data/README.md +237 -0
  17. data/Rakefile +10 -0
  18. data/UPGRADE.md +5 -0
  19. data/VERSIONS.md +35 -0
  20. data/advanced-examples/custom-http-client.md +166 -0
  21. data/examples/examples.rb +23 -0
  22. data/githooks/pre-commit +1 -0
  23. data/lib/rack/reach_webhook_authentication.rb +72 -0
  24. data/lib/reach-ruby/framework/reach_response.rb +19 -0
  25. data/lib/reach-ruby/framework/request.rb +41 -0
  26. data/lib/reach-ruby/framework/response.rb +18 -0
  27. data/lib/reach-ruby/framework/rest/domain.rb +39 -0
  28. data/lib/reach-ruby/framework/rest/error.rb +51 -0
  29. data/lib/reach-ruby/framework/rest/helper.rb +11 -0
  30. data/lib/reach-ruby/framework/rest/page.rb +144 -0
  31. data/lib/reach-ruby/framework/rest/resource.rb +23 -0
  32. data/lib/reach-ruby/framework/rest/version.rb +240 -0
  33. data/lib/reach-ruby/framework/serialize.rb +81 -0
  34. data/lib/reach-ruby/framework/values.rb +9 -0
  35. data/lib/reach-ruby/http/http_client.rb +82 -0
  36. data/lib/reach-ruby/http.rb +5 -0
  37. data/lib/reach-ruby/rest/api/authentix/.openapi-generator/FILES +10 -0
  38. data/lib/reach-ruby/rest/api/authentix/.openapi-generator/VERSION +1 -0
  39. data/lib/reach-ruby/rest/api/authentix/.openapi-generator-ignore +23 -0
  40. data/lib/reach-ruby/rest/api/authentix/authentication_trial_item.rb +418 -0
  41. data/lib/reach-ruby/rest/api/authentix/authentication_trial_stat_item.rb +279 -0
  42. data/lib/reach-ruby/rest/api/authentix/configuration_item/authentication_control_item.rb +214 -0
  43. data/lib/reach-ruby/rest/api/authentix/configuration_item/authentication_item.rb +449 -0
  44. data/lib/reach-ruby/rest/api/authentix/configuration_item.rb +583 -0
  45. data/lib/reach-ruby/rest/api/authentix.rb +72 -0
  46. data/lib/reach-ruby/rest/api/messaging/.openapi-generator/FILES +2 -0
  47. data/lib/reach-ruby/rest/api/messaging/.openapi-generator/VERSION +1 -0
  48. data/lib/reach-ruby/rest/api/messaging/.openapi-generator-ignore +23 -0
  49. data/lib/reach-ruby/rest/api/messaging/messaging_item.rb +582 -0
  50. data/lib/reach-ruby/rest/api/messaging.rb +51 -0
  51. data/lib/reach-ruby/rest/api.rb +50 -0
  52. data/lib/reach-ruby/rest/client.rb +130 -0
  53. data/lib/reach-ruby/rest.rb +13 -0
  54. data/lib/reach-ruby/security/request_validator.rb +149 -0
  55. data/lib/reach-ruby/util/configuration.rb +25 -0
  56. data/lib/reach-ruby/version.rb +3 -0
  57. data/lib/reach-ruby.rb +44 -0
  58. data/reach-ruby.gemspec +38 -0
  59. data/sonar-project.properties +13 -0
  60. metadata +267 -0
@@ -0,0 +1,31 @@
1
+ <!--
2
+ We appreciate the effort for this pull request but before that please make sure you read the contribution guidelines, then fill out the blanks below.
3
+
4
+ Please format the PR title appropriately based on the type of change:
5
+ <type>[!]: <description>
6
+ Where <type> is one of: docs, chore, feat, fix, test, misc.
7
+ Add a '!' after the type for breaking changes (e.g. feat!: new breaking feature).
8
+
9
+ **All third-party contributors acknowledge that any contributions they provide will be made under the same open-source license that the open-source project is provided under.**
10
+
11
+ Please enter each Issue number you are resolving in your PR after one of the following words [Fixes, Closes, Resolves]. This will auto-link these issues and close them when this PR is merged!
12
+ e.g.
13
+ Fixes #1
14
+ Closes #2
15
+ -->
16
+
17
+ # Fixes #
18
+
19
+ A short description of what this PR does.
20
+
21
+ ### Checklist
22
+ - [x] I acknowledge that all my contributions will be made under the project's license
23
+ - [ ] I have made a material change to the repo (functionality, testing, spelling, grammar)
24
+ - [ ] I have read the [Contribution Guidelines](https://github.com/talkylabs/reach-ruby/blob/main/CONTRIBUTING.md) and my PR follows them
25
+ - [ ] I have titled the PR appropriately
26
+ - [ ] I have updated my branch with the main branch
27
+ - [ ] I have added tests that prove my fix is effective or that my feature works
28
+ - [ ] I have added the necessary documentation about the functionality in the appropriate .md file
29
+ - [ ] I have added inline documentation to the code I modified
30
+
31
+ If you have questions, please create a GitHub Issue in this repository.
data/README.md ADDED
@@ -0,0 +1,237 @@
1
+ # reach-ruby
2
+
3
+
4
+ ## Documentation
5
+
6
+ The documentation for the Reach API can be found [here][apidocs].
7
+
8
+ The individual releases [here][refdocs].
9
+
10
+ ## Versions
11
+
12
+ `reach-ruby` uses a modified version of [Semantic Versioning](https://semver.org) for all changes. [See this document](VERSIONS.md) for details.
13
+
14
+ ### Supported Ruby Versions
15
+
16
+ This library supports the following Ruby implementations:
17
+
18
+ - Ruby 2.4
19
+ - Ruby 2.5
20
+ - Ruby 2.6
21
+ - Ruby 2.7
22
+ - Ruby 3.0
23
+ - Ruby 3.1
24
+
25
+ ## Installation
26
+
27
+ To install using [Bundler][bundler] grab the latest stable version:
28
+
29
+ ```ruby
30
+ gem 'reach-ruby', '~> 1.0.0'
31
+ ```
32
+
33
+ To manually install `reach-ruby` via [Rubygems][rubygems] simply gem install:
34
+
35
+ ```bash
36
+ gem install reach-ruby -v 1.0.0
37
+ ```
38
+
39
+ To build and install the development branch yourself from the latest source:
40
+
41
+ ```bash
42
+ git clone git@github.com:talkylabs/reach-ruby.git
43
+ cd reach-ruby
44
+ make install
45
+ ```
46
+
47
+ > **Info**
48
+ > If the command line gives you an error message that says Permission Denied, try running the above commands with sudo.
49
+ >
50
+ > For example: `sudo gem install reach-ruby`
51
+
52
+ ### Test your installation
53
+
54
+ To make sure the installation was successful, try sending yourself an SMS message, like this:
55
+
56
+ ```rb
57
+ require "reach-ruby"
58
+
59
+ # Your API user and key the web app
60
+ apiUser = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
61
+ apiKey = "your_api_key"
62
+
63
+ @client = Reach::REST::Client.new apiUser, apiKey
64
+ message = @client.messaging.messaging_items.dispatch(
65
+ body: "Hello from Ruby",
66
+ dest: "+12345678901", # Text this number
67
+ src: "+15005550006", # From a valid number
68
+ )
69
+
70
+ puts message.messageId
71
+ ```
72
+
73
+ > **Warning**
74
+ > It's okay to hardcode your credentials when testing locally, but you should use environment variables to keep them secret before committing any code or deploying to production.
75
+
76
+ ## Usage
77
+
78
+ ### Authenticate the Client
79
+
80
+ ```ruby
81
+ require 'reach-ruby'
82
+
83
+ # Your API user and key the web app
84
+ apiUser = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
85
+ apiKey = "your_api_key"
86
+
87
+ # Initialize the Reach Client with your credentials
88
+ @client = Reach::REST::Client.new apiUser, apiKey
89
+ ```
90
+
91
+ ### Send an SMS
92
+
93
+ ```ruby
94
+ @client.messaging.messaging_items.dispatch(
95
+ src: '+14159341234',
96
+ dest: '+16105557069',
97
+ body: 'Hey there!'
98
+ )
99
+ ```
100
+
101
+ ### List your SMS Messages
102
+
103
+ ```ruby
104
+ @client.messaging.messaging_items.dispatch.list(limit: 20)
105
+ ```
106
+
107
+ ### Fetch a single SMS message by messageId
108
+
109
+ ```ruby
110
+ # put the message sid you want to retrieve here:
111
+ messageId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
112
+ @client.messaging.messaging_items(messageId).fetch
113
+ ```
114
+
115
+ ### Iterate through records
116
+
117
+ The library automatically handles paging for you. Collections, such as `messaging_items`, have `list` and stream methods that page under the hood. With both `list` and `stream`, you can specify the number of records you want to receive (`limit`) and the maximum size you want each page fetch to be (`page_size`). The library will then handle the task for you.
118
+
119
+ `list` eagerly fetches all records and returns them as a list, whereas `stream` returns an enumerator and lazily retrieves pages of records as you iterate over the collection. You can also page manually using the `page` method.
120
+
121
+
122
+ ```rb
123
+ require 'reach-ruby'
124
+
125
+ # Your API user and key the web app
126
+ apiUser = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
127
+ apiKey = "your_api_key"
128
+
129
+ @client = Reach::REST::Client.new(apiUser, apiKey)
130
+
131
+ @client.messaging.messaging_items.list
132
+ .each do |msg|
133
+ puts msg.messageId
134
+ end
135
+ ```
136
+
137
+ ### Enable Debug logging
138
+
139
+ In order to enable debug logging, pass in a 'logger' instance to the client with the level set to at least 'DEBUG'
140
+
141
+ ```ruby
142
+ @client = Reach::REST::Client.new apiUser, apiKey
143
+ myLogger = Logger.new(STDOUT)
144
+ myLogger.level = Logger::DEBUG
145
+ @client.logger = myLogger
146
+
147
+ @client = Reach::REST::Client.new apiUser, apiKey
148
+ myLogger = Logger.new('my_log.log')
149
+ myLogger.level = Logger::DEBUG
150
+ @client.logger = myLogger
151
+ ```
152
+
153
+ ### Handle Exceptions {#exceptions}
154
+
155
+ If the Reach API returns a 400 or a 500 level HTTP response, the `reach-ruby`
156
+ library will throw a `Reach::REST::RestError`. 400-level errors are normal
157
+ during API operation (`“Invalid number”`, `“Cannot deliver SMS to that number”`,
158
+ for example) and should be handled appropriately.
159
+
160
+ ```rb
161
+ require 'reach-ruby'
162
+
163
+ # Your API user and key the web app
164
+ apiUser = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
165
+ apiKey = "your_api_key"
166
+
167
+ @client = Reach::REST::Client.new apiUser, apiKey
168
+
169
+ begin
170
+ messages = @client.messaging.messaging_items.list(limit: 20)
171
+ rescue Reach::REST::RestError => e
172
+ puts e.message
173
+ end
174
+ ```
175
+
176
+ ### Debug API requests
177
+
178
+ To assist with debugging, the library allows you to access the underlying request and response objects. This capability is built into the default HTTP client that ships with the library.
179
+
180
+ For example, you can retrieve the status code of the last response like so:
181
+
182
+ ```ruby
183
+ require 'rubygems' # Not necessary with ruby 1.9 but included for completeness
184
+ require 'reach-ruby'
185
+
186
+ # Your API user and key the web app
187
+ apiUser = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
188
+ apiKey = "your_api_key"
189
+
190
+ @client = Reach::REST::Client.new(apiUser, apiKey)
191
+
192
+ @message = @client.messaging.messaging_items.dispatch(
193
+ dest: '+14158675309',
194
+ src: '+14258675310',
195
+ body: 'Ahoy!'
196
+ )
197
+
198
+ # Retrieve the status code of the last response from the HTTP client
199
+ puts @client.http_client.last_response.status_code
200
+ ```
201
+
202
+ ### Customize your HTTP Client
203
+
204
+ `reach-ruby` uses [Faraday][faraday] to make HTTP requests. You can tell `Reach::REST::Client` to use any of the Faraday adapters like so:
205
+
206
+ ```ruby
207
+ @client.http_client.adapter = :typhoeus
208
+ ```
209
+
210
+ To use a custom HTTP client with this helper library, please see the [advanced example of how to do so](./advanced-examples/custom-http-client.md).
211
+
212
+ To apply customizations such as middleware, you can use the `configure_connection` method like so:
213
+
214
+ ```ruby
215
+ @client.http_client.configure_connection do |faraday|
216
+ faraday.use SomeMiddleware
217
+ end
218
+ ```
219
+
220
+
221
+
222
+ ## Docker Image
223
+
224
+ The `Dockerfile` present in this repository and its respective `talkylabs/reach-ruby` Docker image are currently used by Us for testing purposes only.
225
+
226
+ ## Getting help
227
+
228
+ If you've instead found a bug in the library or would like new features added, go ahead and open issues or pull requests against this repo!
229
+
230
+ [apidocs]: https://www.reach.talkylabs.com/docs/api
231
+ [refdocs]: https://talkylabs.github.io/reach-ruby
232
+ [wiki]: https://github.com/talkylabs/reach-ruby/wiki
233
+ [bundler]: https://bundler.io
234
+ [rubygems]: https://rubygems.org
235
+ [gem]: https://rubygems.org/gems/talkylabs
236
+ [issues]: https://github.com/talkylabs/reach-ruby/issues
237
+ [faraday]: https://github.com/lostisland/faraday
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require 'rspec/core/rake_task'
6
+ desc 'Run all specs'
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task default: :spec
10
+ task test: :spec
data/UPGRADE.md ADDED
@@ -0,0 +1,5 @@
1
+ # Upgrade Guide
2
+
3
+ _All `MINOR` and `MAJOR` version bumps will have upgrade notes
4
+ posted here._
5
+
data/VERSIONS.md ADDED
@@ -0,0 +1,35 @@
1
+ # Versioning Strategy
2
+
3
+ `reach-ruby` uses a modified version of [Semantic Versioning][semver] for
4
+ all changes to the helper library. It is strongly encouraged that you pin at
5
+ least the major version and potentially the minor version to avoid pulling in
6
+ breaking changes.
7
+
8
+ Semantic Versions take the form of `MAJOR.MINOR.PATCH`
9
+
10
+ When bugs are fixed in the library in a backwards-compatible way, the `PATCH`
11
+ level will be incremented by one. When new features are added to the library
12
+ in a backwards-compatible way, the `PATCH` level will be incremented by one.
13
+ `PATCH` changes should _not_ break your code and are generally safe for upgrade.
14
+
15
+ When a new large feature set comes online or a small breaking change is
16
+ introduced, the `MINOR` version will be incremented by one and the `PATCH`
17
+ version reset to zero. `MINOR` changes _may_ require some amount of manual code
18
+ change for upgrade. These backwards-incompatible changes will generally be
19
+ limited to a small number of function signature changes.
20
+
21
+ The `MAJOR` version is used to indicate the family of technology represented by
22
+ the helper library. Breaking changes that require extensive reworking of code
23
+ will cause the `MAJOR` version to be incremented by one, and the `MINOR` and
24
+ `PATCH` versions will be reset to zero. We understands that this can be very
25
+ disruptive, so we will only introduce this type of breaking change when
26
+ absolutely necessary. New `MAJOR` versions will be communicated in advance with
27
+ `Release Candidates` and a schedule.
28
+
29
+ ## Supported Versions
30
+
31
+ Only the current `MAJOR` version of `reach-ruby` is supported. New
32
+ features, functionality, bug fixes, and security updates will only be added to
33
+ the current `MAJOR` version.
34
+
35
+ [semver]: https://semver.org
@@ -0,0 +1,166 @@
1
+ # Custom HTTP Clients for the Reach Ruby Helper Library
2
+
3
+ If you are working with the Reach Ruby Helper Library, and you need to be able to modify the HTTP requests that the library makes to the Reach servers, you’re in the right place. The most common need to alter the HTTP request is to connect and authenticate with an enterprise’s proxy server. We’ll provide sample code that you can drop right into your app to handle this use case.
4
+
5
+ Connect and authenticate with a proxy server
6
+ To connect and provide credentials to a proxy server that may be between your app and Reach, you need a way to modify the HTTP requests that the Reach helper library makes on your behalf to invoke the Reach REST API.
7
+
8
+ The Reach Ruby helper library uses the [Faraday](https://rubygems.org/gems/faraday) gem under the hood to make the HTTP requests. The following example shows a typical request, without a custom `http_client`:
9
+
10
+ ```rb
11
+ @client = Reach::REST::Client.new(username, auth_token)
12
+
13
+ message = @client.messaging.messaging_items
14
+ .dispatch(
15
+ dest: "+15558675310",
16
+ body: "Hey there!",
17
+ src: "+15017122661",
18
+ )
19
+ ```
20
+
21
+ Out of the box, the helper library is creating a default `Reach::Http::Client` for you, using the Reach credentials you provide. However, you can create your own `Reach::Http::Client`, and pass it to any Reach REST API resource action you want.
22
+
23
+ Here’s an example of sending an SMS message with a custom client:
24
+
25
+ ```rb
26
+ require "rubygems"
27
+ require "reach-ruby"
28
+ require "dotenv/load"
29
+
30
+ # Custom HTTP Client
31
+ require_relative "MyRequestClass"
32
+
33
+ # Your username and Auth Token from the web application
34
+ username = ENV["API_USER"]
35
+ auth_token = ENV["API_KEY"]
36
+ proxy_address = ENV["PROXY_ADDRESS"]
37
+ proxy_protocol = ENV["PROXY_PROTOCOL"]
38
+ proxy_port = ENV["PROXY_PORT"]
39
+
40
+ my_request_client = MyRequestClass.new(proxy_protocol, proxy_address, proxy_port)
41
+
42
+ @client = Reach::REST::Client.new(username, auth_token, my_request_client)
43
+
44
+ message = @client.messaging.messaging_items
45
+ .dispatch(
46
+ dest: "+593978613041",
47
+ body: "RB This is the ship that made the Kesssssel Run in fourteen parsecs?",
48
+ src: "+13212855389",
49
+ )
50
+
51
+ puts "Message SID: #{message.messageId}"
52
+ ```
53
+
54
+ ## Create your custom ReachRestClient
55
+
56
+ When you take a closer look at the constructor for `Reach::Http::Client`, you see that this class provides it to the Reach helper library to make the necessary HTTP requests.
57
+
58
+ ## Call Reach through the proxy server
59
+
60
+ Now that we understand how all the components fit together, we can create our own `http_client` that can connect through a proxy server. To make this reusable, here’s a class that you can use to create this `http_client` whenever you need one.
61
+
62
+ ```rb
63
+ class MyRequestClass
64
+ attr_accessor :adapter
65
+ attr_reader :timeout, :last_response, :last_request
66
+
67
+ def initialize(proxy_prot = nil, proxy_addr = nil, proxy_port = nil, timeout: nil)
68
+ @proxy_prot = proxy_prot
69
+ @proxy_addr = proxy_addr
70
+ @proxy_port = proxy_port
71
+ @timeout = timeout
72
+ @adapter = Faraday.default_adapter
73
+ end
74
+
75
+ def _request(request)
76
+ @connection = Faraday.new(url: request.host + ":" + request.port.to_s, ssl: { verify: true }) do |f|
77
+ f.options.params_encoder = Faraday::FlatParamsEncoder
78
+ f.request :url_encoded
79
+ f.adapter @adapter
80
+ f.headers = request.headers
81
+ f.headers["ApiUser"] = request.auth[0]
82
+ f.headers["ApiKey"] = request.auth[1]
83
+ if @proxy_addr
84
+ f.proxy = "#{@proxy_prot}://#{@proxy_addr}:#{@proxy_port}"
85
+ end
86
+ f.options.open_timeout = request.timeout || @timeout
87
+ f.options.timeout = request.timeout || @timeout
88
+ end
89
+
90
+ @last_request = request
91
+ @last_response = nil
92
+ response = @connection.send(request.method.downcase.to_sym,
93
+ request.url,
94
+ request.method == "GET" ? request.params : request.data)
95
+
96
+ if response.body && !response.body.empty?
97
+ object = response.body
98
+ elsif response.status == 400
99
+ object = { message: "Bad request", code: 400 }.to_json
100
+ end
101
+
102
+ reach_response = Reach::Response.new(response.status, object, headers: response.headers)
103
+ @last_response = reach_response
104
+
105
+ reach_response
106
+ end
107
+
108
+ def request(host, port, method, url, params = {}, data = {}, headers = {}, auth = nil, timeout = nil)
109
+ request = Reach::Request.new(host, port, method, url, params, data, headers, auth, timeout)
110
+ _request(request)
111
+ end
112
+ end
113
+ ```
114
+
115
+ In this example, we are using some environment variables loaded at the program startup to retrieve various configuration settings:
116
+
117
+ - Your Reach Api User and Api Key
118
+ - A proxy address in the form of `http://127.0.0.1:8888`
119
+
120
+ These settings are located in a file like `.env` like so:
121
+
122
+ ```env
123
+ API_USER=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
124
+ API_KEY= your_auth_token
125
+
126
+ HTTPS_PROXY=https://127.0.0.1:8888
127
+ HTTP_PROXY=http://127.0.0.1:8888
128
+ ```
129
+
130
+ Here’s the full console program that sends a text message and shows how it all can work together. It loads the `.env` file.
131
+
132
+ ```rb
133
+ require "rubygems"
134
+ require "reach-ruby"
135
+ require "dotenv/load"
136
+
137
+ # Custom HTTP Client
138
+ require_relative "MyRequestClass"
139
+
140
+ username = ENV["API_USER"]
141
+ auth_token = ENV["API_KEY"]
142
+ proxy_address = ENV["PROXY_ADDRESS"]
143
+ proxy_protocol = ENV["PROXY_PROTOCOL"]
144
+ proxy_port = ENV["PROXY_PORT"]
145
+
146
+ my_request_client = MyRequestClass.new(proxy_protocol, proxy_address, proxy_port)
147
+
148
+ @client = Reach::REST::Client.new(username, auth_token, my_request_client)
149
+
150
+ message = @client.messaging.messaging_items
151
+ .dispatch(
152
+ dest: "+593978613041",
153
+ body: "RB This is the ship that made the Kesssssel Run in fourteen parsecs?",
154
+ src: "+13212855389",
155
+ )
156
+
157
+ puts "Message SID: #{message.messageId}"
158
+ ```
159
+
160
+ ## What else can this technique be used for?
161
+
162
+ Now that you know how to inject your own `http_client` into the Reach API request pipeline, you could use this technique to add custom HTTP headers and authorization to the requests (perhaps as required by an upstream proxy server).
163
+
164
+ You could also implement your own `http_client` to mock the Reach API responses so your unit and integration tests can run quickly without the need to make a connection to Reach.
165
+
166
+ We can’t wait to see what you build!
@@ -0,0 +1,23 @@
1
+ # examples version
2
+
3
+ @username = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
4
+ @auth_token = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
5
+ # set up a client
6
+ @client = Reach::REST::Client.new(@username, @auth_token)
7
+
8
+ ################ SMS MESSAGES ################
9
+
10
+ @client.messaging.messaging_items.list(sent_at: '2010-09-01').each do |message|
11
+ puts message.dateCreated
12
+ end
13
+
14
+ # print a particular sms message
15
+ puts @client.messaging.messaging_items('SMXXXXXXXX').fetch.body
16
+
17
+ # send an sms
18
+ @client.messaging.messaging_items.dispatch(
19
+ src: '+14159341234',
20
+ dest: '+16105557069',
21
+ body: 'Hey there!'
22
+ )
23
+
@@ -0,0 +1 @@
1
+ make test
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/media_type'
4
+
5
+ module Rack
6
+ # Middleware that authenticates webhooks from Reach using the request
7
+ # validator.
8
+ #
9
+ # The middleware takes an auth token with which to set up the request
10
+ # validator and any number of paths. When a path matches the incoming request
11
+ # path, the request will be checked for authentication.
12
+ #
13
+ # Example:
14
+ #
15
+ # require 'rack'
16
+ # use Rack::ReachWebhookAuthentication, ENV['REACH_TALKYLABS_API_KEY'], /\/messages/
17
+ #
18
+ # The above appends this middleware to the stack, using an auth token saved in
19
+ # the ENV and only against paths that match /\/messages/. If the request
20
+ # validates then it gets passed on to the action as normal. If the request
21
+ # doesn't validate then the middleware responds immediately with a 403 status.
22
+
23
+ class ReachWebhookAuthentication
24
+ # Rack's FORM_DATA_MEDIA_TYPES can be modified to taste, so we're slightly
25
+ # more conservative in what we consider form data.
26
+ FORM_URLENCODED_MEDIA_TYPE = Rack::MediaType.type('application/x-www-form-urlencoded')
27
+
28
+ def initialize(app, auth_token, *paths, &auth_token_lookup)
29
+ @app = app
30
+ @auth_token = auth_token
31
+ define_singleton_method(:get_auth_token, auth_token_lookup) if block_given?
32
+ @path_regex = Regexp.union(paths)
33
+ end
34
+
35
+ def call(env)
36
+ return @app.call(env) unless env['PATH_INFO'].match(@path_regex)
37
+ request = Rack::Request.new(env)
38
+ original_url = request.url
39
+ params = extract_params!(request)
40
+ auth_token = @auth_token || get_auth_token(params['ApiUser'])
41
+ validator = Reach::Security::RequestValidator.new(auth_token)
42
+ signature = env['HTTP_X_REACH_SIGNATURE'] || ''
43
+ if validator.validate(original_url, params, signature)
44
+ @app.call(env)
45
+ else
46
+ [
47
+ 403,
48
+ { 'Content-Type' => 'text/plain' },
49
+ ['Reach Request Validation Failed.']
50
+ ]
51
+ end
52
+ end
53
+
54
+ # Extract the params from the the request that we can use to determine the
55
+ # signature. This _may_ modify the passed in request since it may read/rewind
56
+ # the body.
57
+ def extract_params!(request)
58
+ return {} unless request.post?
59
+
60
+ if request.media_type == FORM_URLENCODED_MEDIA_TYPE
61
+ request.POST
62
+ else
63
+ request.body.rewind
64
+ body = request.body.read
65
+ request.body.rewind
66
+ body
67
+ end
68
+ end
69
+
70
+ private :extract_params!
71
+ end
72
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reach
4
+ class ReachResponse
5
+ attr_accessor :status_code, :body
6
+
7
+ # @deprecated Use 'Reach::Response' instead.
8
+ def initialize(status_code, body)
9
+ warn "'Reach::ReachResponse' has been deprecated. Use 'Reach::Response' instead."
10
+ response = Reach::Response.new(status_code, body)
11
+ @status_code = response.status_code
12
+ @body = response.body
13
+ end
14
+
15
+ def to_s
16
+ "[#{@status_code}] #{@body}"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reach
4
+ class Request
5
+ attr_reader :host, :port, :method, :url, :params, :data, :headers, :auth, :timeout
6
+
7
+ def initialize(host, port, method, url, params = {}, data = {}, headers = {}, auth = nil, timeout = nil)
8
+ @host = host
9
+ @port = port
10
+ @url = url
11
+ @method = method
12
+ @params = params
13
+ @data = data
14
+ @headers = headers
15
+ @auth = auth
16
+ @timeout = timeout
17
+ end
18
+
19
+ def to_s
20
+ auth = @auth.nil? ? '' : '(' + @auth.join(',') + ')'
21
+
22
+ params = ''
23
+ unless @params.nil? || @params.empty?
24
+ params = '?' + @params.each.map { |key, value| "#{CGI.escape(key)}=#{CGI.escape(value)}" }.join('&')
25
+ end
26
+
27
+ headers = ''
28
+ unless @headers.nil? || @headers.empty?
29
+ headers = "\n" + @headers.each.map { |key, value| "-H \"#{key}\": \"#{value}\"" }.join("\n")
30
+ end
31
+
32
+ data = ''
33
+ unless @data.nil? || @data.empty?
34
+ data = @method.equal?('GET') ? "\n -G" : "\n"
35
+ data += @data.each.map { |key, value| "-d \"#{key}\"=\"#{value}\"" }.join("\n")
36
+ end
37
+
38
+ "#{auth} #{@method} #{@url}#{params}#{data}#{headers}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reach
4
+ class Response
5
+ attr_accessor :status_code, :body, :headers
6
+
7
+ def initialize(status_code, body, headers: nil)
8
+ @status_code = status_code
9
+ body = '{}' if !body || body.empty?
10
+ @body = JSON.parse(body)
11
+ @headers = !headers ? {} : headers.to_hash
12
+ end
13
+
14
+ def to_s
15
+ "[#{status_code}] #{body}"
16
+ end
17
+ end
18
+ end