test-rc-sdk 1.0.0
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/README.md +190 -0
- data/lib/ringcentral.rb +227 -0
- data/lib/subscription.rb +66 -0
- data/spec/fax_spec.rb +26 -0
- data/spec/mms_spec.rb +30 -0
- data/spec/query_params_spec.rb +25 -0
- data/spec/ringcentral_spec.rb +86 -0
- data/spec/websocket_subscription_spec.rb +76 -0
- data/test-rc-sdk.gemspec +21 -0
- metadata +157 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f0ed544553943eef59bca4060e254a266591ca821c64e31e9d6b18f70c770aa6
|
4
|
+
data.tar.gz: b59c797191c98112301c069ed94850708c2a41868cade83fcafa9ae833b1fc71
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e5ea09c8f9b261383ba26ad46987a9ab653e09a550ee81d48bf316119d857060f9031633339b7b3f3b9985f05525912579650ed40ad28b51394f1bbe88493bbe
|
7
|
+
data.tar.gz: d5a62a30adfdaa7835007fb6377e4414c9d7630dc9e9ebd5e631a417e2777ee4c7211897cb03387d94f85ebe82f2d97ab0d43591967ace3da6ed4d6ee4106982
|
data/README.md
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
# RingCentral SDK for Ruby
|
2
|
+
|
3
|
+
[](https://github.com/ringcentral/ringcentral-ruby/actions/workflows/ruby.yml)
|
4
|
+
[](https://ringcentral.github.io/ringcentral-ruby/)
|
5
|
+
[](https://twitter.com/RingCentralDevs)
|
6
|
+
|
7
|
+
__[RingCentral Developers](https://developer.ringcentral.com/api-products)__ is a cloud communications platform which can be accessed via more than 70 APIs. The platform's main capabilities include technologies that enable:
|
8
|
+
__[Voice](https://developer.ringcentral.com/api-products/voice), [SMS/MMS](https://developer.ringcentral.com/api-products/sms), [Fax](https://developer.ringcentral.com/api-products/fax), [Glip Team Messaging](https://developer.ringcentral.com/api-products/team-messaging), [Data and Configurations](https://developer.ringcentral.com/api-products/configuration)__.
|
9
|
+
|
10
|
+
## Additional resources
|
11
|
+
|
12
|
+
* [RingCentral API Reference](https://developer.ringcentral.com/api-docs/latest/index.html) - an interactive reference for the RingCentral API that allows developers to make API calls with no code.
|
13
|
+
* [Document](https://ringcentral.github.io/ringcentral-ruby/) - an interactive reference for the SDK code documentation.
|
14
|
+
|
15
|
+
|
16
|
+
## Getting help and support
|
17
|
+
|
18
|
+
If you are having difficulty using this SDK, or working with the RingCentral API, please visit our [developer community forums](https://community.ringcentral.com/spaces/144/) for help and to get quick answers to your questions. If you wish to contact the RingCentral Developer Support team directly, please [submit a help ticket](https://developers.ringcentral.com/support/create-case) from our developer website.
|
19
|
+
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
```
|
24
|
+
gem install ringcentral-sdk
|
25
|
+
```
|
26
|
+
|
27
|
+
If for some reason `eventmachine` failed to install, please check [this](https://stackoverflow.com/a/31516586/862862).
|
28
|
+
|
29
|
+
|
30
|
+
### Name collision with `ringcentral` gem
|
31
|
+
|
32
|
+
The `ringcentral` gem is using RingCentral's legacy API which was End-of-Lifed in 2018. Everyone is recommended to move to the REST API.
|
33
|
+
|
34
|
+
If you have both the `ringcentral` and `ringcentral-sdk` gems installed, you will run into a collision error when attempting to initialize the `ringcentral-sdk` RingCentral SDK.
|
35
|
+
|
36
|
+
The solution is `gem uninstall ringcentral`
|
37
|
+
|
38
|
+
|
39
|
+
## Documentation
|
40
|
+
|
41
|
+
https://developer.ringcentral.com/api-docs/latest/index.html
|
42
|
+
|
43
|
+
|
44
|
+
## Usage
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
require 'ringcentral'
|
48
|
+
|
49
|
+
rc = RingCentral.new('clientID', 'clientSecret', 'serverURL')
|
50
|
+
rc.authorize(jwt: 'jwt-token')
|
51
|
+
|
52
|
+
# get
|
53
|
+
r = rc.get('/restapi/v1.0/account/~/extension/~')
|
54
|
+
expect(r).not_to be_nil
|
55
|
+
expect('101').to eq(r.body['extensionNumber'])
|
56
|
+
```
|
57
|
+
|
58
|
+
|
59
|
+
## How to specify query parameters
|
60
|
+
|
61
|
+
### for get & delete
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
rc.get('/restapi/v1.0/account/~/extension', { hello: 'world' })
|
65
|
+
```
|
66
|
+
|
67
|
+
### for post, put & patch
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
rc.post('/restapi/v1.0/account/~/extension/~/sms', payload: body, params: { hello: 'world' })
|
71
|
+
```
|
72
|
+
|
73
|
+
### multi-value query parameter
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
rc.get('/restapi/v1.0/account/~/extension', { hello: ['world1', 'world2'] })
|
77
|
+
```
|
78
|
+
|
79
|
+
Above will be translated to `/restapi/v1.0/account/~/extension?hello=world1&hello=world2`.
|
80
|
+
|
81
|
+
|
82
|
+
### Token Refresh
|
83
|
+
|
84
|
+
Access token expires. You need to call `rc.refresh()` before it expires.
|
85
|
+
If you want the SDK to do auto refresh please `rc.auto_refresh = true` before authorization.
|
86
|
+
|
87
|
+
|
88
|
+
### Load pre-existing token
|
89
|
+
|
90
|
+
Let's say you already have a token. Then you can load it like this: `rc.token = your_token_object`.
|
91
|
+
The benefit of loading a preexisting token is you don't need to go through any authorization flow.
|
92
|
+
|
93
|
+
If what you have is a JSON string instead of a Ruby object, you need to convert it first: `JSON.parse(your_token_string)`.
|
94
|
+
|
95
|
+
If you only have a string for the access token instead of for the whole object, you can set it like this:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
rc.token = { access_token: 'the token string' }
|
99
|
+
```
|
100
|
+
|
101
|
+
|
102
|
+
### Send SMS
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
r = rc.post('/restapi/v1.0/account/~/extension/~/sms', payload: {
|
106
|
+
to: [{phoneNumber: ENV['RINGCENTRAL_RECEIVER']}],
|
107
|
+
from: {phoneNumber: ENV['RINGCENTRAL_SENDER']},
|
108
|
+
text: 'Hello world'
|
109
|
+
})
|
110
|
+
```
|
111
|
+
|
112
|
+
|
113
|
+
### Send fax
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
rc.post('/restapi/v1.0/account/~/extension/~/fax',
|
117
|
+
payload: { to: [{ phoneNumber: ENV['RINGCENTRAL_RECEIVER'] }] },
|
118
|
+
files: [
|
119
|
+
['spec/test.txt', 'text/plain'],
|
120
|
+
['spec/test.png', 'image/png']
|
121
|
+
]
|
122
|
+
)
|
123
|
+
```
|
124
|
+
|
125
|
+
|
126
|
+
### Send MMS
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
r = rc.post('/restapi/v1.0/account/~/extension/~/sms',
|
130
|
+
payload: {
|
131
|
+
to: [{ phoneNumber: ENV['RINGCENTRAL_RECEIVER'] }],
|
132
|
+
from: { phoneNumber: ENV['RINGCENTRAL_SENDER'] },
|
133
|
+
text: 'hello world'
|
134
|
+
},
|
135
|
+
files: [
|
136
|
+
['spec/test.png', 'image/png']
|
137
|
+
]
|
138
|
+
)
|
139
|
+
```
|
140
|
+
|
141
|
+
|
142
|
+
## Subscriptions
|
143
|
+
|
144
|
+
### WebSocket Subscriptions
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
events = [
|
148
|
+
'/restapi/v1.0/account/~/extension/~/message-store',
|
149
|
+
]
|
150
|
+
subscription = WS.new(rc, events, lambda { |message|
|
151
|
+
puts message
|
152
|
+
})
|
153
|
+
subscription.subscribe()
|
154
|
+
```
|
155
|
+
|
156
|
+
#### How to keep a subscription running 24 * 7?
|
157
|
+
|
158
|
+
There are two main cases that a subscription will be terminated:
|
159
|
+
|
160
|
+
- Absolute time out. The maximum time for a subscription to run is 24 hours. After that, the websocket connection will be closed by the server.
|
161
|
+
- Network issue. It could be your local network issue or the server's network issue. In either case, your websocket connection will be closed
|
162
|
+
|
163
|
+
In order to keep a subscription running 24 * 7, you need to re-subscribe when the connection is closed.
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
subscription.on_ws_closed = lambda { |event|
|
167
|
+
# make sure that there is no network issue and re-subscribe
|
168
|
+
subscription.subscribe()
|
169
|
+
}
|
170
|
+
```
|
171
|
+
|
172
|
+
|
173
|
+
## How to test
|
174
|
+
|
175
|
+
```
|
176
|
+
bundle install
|
177
|
+
```
|
178
|
+
|
179
|
+
Rename `.env.sample` to `.env`.
|
180
|
+
|
181
|
+
Edit `.env` file to specify credentials.
|
182
|
+
|
183
|
+
`RINGCENTRAL_RECEIVER` is a phone number to receive SMS, Fax..etc.
|
184
|
+
|
185
|
+
Run `bundle exec rspec`
|
186
|
+
|
187
|
+
|
188
|
+
## License
|
189
|
+
|
190
|
+
MIT
|
data/lib/ringcentral.rb
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'addressable/uri'
|
3
|
+
require 'json'
|
4
|
+
require 'concurrent'
|
5
|
+
require 'faraday'
|
6
|
+
require 'faraday/multipart'
|
7
|
+
|
8
|
+
class RingCentral
|
9
|
+
def self.PRODUCTION_SERVER
|
10
|
+
'https://platform.ringcentral.com'
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :client_id, :client_secret, :server, :token
|
14
|
+
attr_accessor :auto_refresh, :debug_mode
|
15
|
+
|
16
|
+
def initialize(client_id, client_secret, server = self.PRODUCTION_SERVER, debug_mode = false)
|
17
|
+
@client_id = client_id
|
18
|
+
@client_secret = client_secret
|
19
|
+
@server = server
|
20
|
+
@auto_refresh = false
|
21
|
+
@debug_mode = debug_mode
|
22
|
+
@token = nil
|
23
|
+
@timer = nil
|
24
|
+
@faraday = Faraday.new(url: server, request: { params_encoder: Faraday::FlatParamsEncoder }) do |faraday|
|
25
|
+
faraday.request :multipart
|
26
|
+
faraday.request :url_encoded
|
27
|
+
faraday.response :json, content_type: /\bjson$/
|
28
|
+
faraday.adapter Faraday.default_adapter
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def token=(value)
|
33
|
+
@token = value
|
34
|
+
if @timer != nil
|
35
|
+
@timer.shutdown
|
36
|
+
@timer = nil
|
37
|
+
end
|
38
|
+
if @auto_refresh && value != nil
|
39
|
+
@timer = Concurrent::TimerTask.new(execution_interval: value['expires_in'] - 120, timeout_interval: 60) { self.refresh }
|
40
|
+
@timer.execute
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def authorize(username: nil, extension: nil, password: nil, auth_code: nil, redirect_uri: nil, jwt: nil, verifier: nil)
|
45
|
+
if auth_code != nil
|
46
|
+
payload = {
|
47
|
+
grant_type: 'authorization_code',
|
48
|
+
code: auth_code,
|
49
|
+
redirect_uri: redirect_uri,
|
50
|
+
}
|
51
|
+
if verifier != nil
|
52
|
+
payload["code_verifier"] = verifier
|
53
|
+
end
|
54
|
+
elsif jwt != nil
|
55
|
+
payload = {
|
56
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
57
|
+
assertion: jwt
|
58
|
+
}
|
59
|
+
else
|
60
|
+
warn('Password auth is deprecated. Use JWT auth or OAuth instead.')
|
61
|
+
payload = {
|
62
|
+
grant_type: 'password',
|
63
|
+
username: username,
|
64
|
+
extension: extension,
|
65
|
+
password: password,
|
66
|
+
}
|
67
|
+
end
|
68
|
+
self.token = nil
|
69
|
+
r = self.post('/restapi/oauth/token', payload: payload)
|
70
|
+
self.token = r.body
|
71
|
+
end
|
72
|
+
|
73
|
+
def refresh
|
74
|
+
return if @token == nil
|
75
|
+
payload = {
|
76
|
+
grant_type: 'refresh_token',
|
77
|
+
refresh_token: @token['refresh_token']
|
78
|
+
}
|
79
|
+
self.token = nil
|
80
|
+
r = self.post('/restapi/oauth/token', payload: payload)
|
81
|
+
self.token = r.body
|
82
|
+
end
|
83
|
+
|
84
|
+
def revoke
|
85
|
+
return if @token == nil
|
86
|
+
payload = { token: @token['access_token'] }
|
87
|
+
self.token = nil
|
88
|
+
self.post('/restapi/oauth/revoke', payload: payload)
|
89
|
+
end
|
90
|
+
|
91
|
+
def authorize_uri(redirect_uri, hash = {})
|
92
|
+
hash[:response_type] = 'code'
|
93
|
+
hash[:redirect_uri] = redirect_uri
|
94
|
+
hash[:client_id] = @client_id
|
95
|
+
uri = Addressable::URI.parse(@server) + '/restapi/oauth/authorize'
|
96
|
+
uri.query_values = hash
|
97
|
+
uri.to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
def get(endpoint, params = {})
|
101
|
+
r = @faraday.get do |req|
|
102
|
+
req.url endpoint
|
103
|
+
req.params = params
|
104
|
+
req.headers = headers
|
105
|
+
end
|
106
|
+
if @debug_mode
|
107
|
+
puts r.status, r.body
|
108
|
+
end
|
109
|
+
if r.status >= 400
|
110
|
+
raise "HTTP status #{r.status}:
|
111
|
+
|
112
|
+
headers: #{r.headers}
|
113
|
+
|
114
|
+
body: #{r.body}"
|
115
|
+
end
|
116
|
+
return r
|
117
|
+
end
|
118
|
+
|
119
|
+
def post(endpoint, payload: nil, params: {}, files: nil)
|
120
|
+
r = @faraday.post do |req|
|
121
|
+
req.url endpoint
|
122
|
+
req.params = params
|
123
|
+
if files != nil && files.size > 0 # send fax or MMS
|
124
|
+
io = StringIO.new(payload.to_json)
|
125
|
+
payload = {}
|
126
|
+
payload[:json] = Faraday::UploadIO.new(io, 'application/json')
|
127
|
+
payload[:attachment] = files.map{ |file| Faraday::UploadIO.new(file[0], file[1]) }
|
128
|
+
req.headers = headers
|
129
|
+
req.body = payload
|
130
|
+
elsif payload != nil && @token != nil
|
131
|
+
req.headers = headers.merge({ 'Content-Type': 'application/json' })
|
132
|
+
req.body = payload.to_json
|
133
|
+
else
|
134
|
+
req.headers = headers
|
135
|
+
req.body = payload
|
136
|
+
end
|
137
|
+
end
|
138
|
+
if @debug_mode
|
139
|
+
puts r.status, r.body
|
140
|
+
end
|
141
|
+
if r.status >= 400
|
142
|
+
raise "HTTP status #{r.status}:
|
143
|
+
|
144
|
+
headers: #{r.headers}
|
145
|
+
|
146
|
+
body: #{r.body}"
|
147
|
+
end
|
148
|
+
return r
|
149
|
+
end
|
150
|
+
|
151
|
+
def put(endpoint, payload: nil, params: {}, files: nil)
|
152
|
+
r = @faraday.put do |req|
|
153
|
+
req.url endpoint
|
154
|
+
req.params = params
|
155
|
+
req.headers = headers.merge({ 'Content-Type': 'application/json' })
|
156
|
+
req.body = payload.to_json
|
157
|
+
end
|
158
|
+
if @debug_mode
|
159
|
+
puts r.status, r.body
|
160
|
+
end
|
161
|
+
if r.status >= 400
|
162
|
+
raise "HTTP status #{r.status}:
|
163
|
+
|
164
|
+
headers: #{r.headers}
|
165
|
+
|
166
|
+
body: #{r.body}"
|
167
|
+
end
|
168
|
+
return r
|
169
|
+
end
|
170
|
+
|
171
|
+
def patch(endpoint, payload: nil, params: {}, files: nil)
|
172
|
+
r = @faraday.patch do |req|
|
173
|
+
req.url endpoint
|
174
|
+
req.params = params
|
175
|
+
req.headers = headers.merge({ 'Content-Type': 'application/json' })
|
176
|
+
req.body = payload.to_json
|
177
|
+
end
|
178
|
+
if @debug_mode
|
179
|
+
puts r.status, r.body
|
180
|
+
end
|
181
|
+
if r.status >= 400
|
182
|
+
raise "HTTP status #{r.status}:
|
183
|
+
|
184
|
+
headers: #{r.headers}
|
185
|
+
|
186
|
+
body: #{r.body}"
|
187
|
+
end
|
188
|
+
return r
|
189
|
+
end
|
190
|
+
|
191
|
+
def delete(endpoint, params = {})
|
192
|
+
r = @faraday.delete do |req|
|
193
|
+
req.url endpoint
|
194
|
+
req.params = params
|
195
|
+
req.headers = headers
|
196
|
+
end
|
197
|
+
if @debug_mode
|
198
|
+
puts r.status, r.body
|
199
|
+
end
|
200
|
+
if r.status >= 400
|
201
|
+
raise "HTTP status #{r.status}:
|
202
|
+
|
203
|
+
headers: #{r.headers}
|
204
|
+
|
205
|
+
body: #{r.body}"
|
206
|
+
end
|
207
|
+
return r
|
208
|
+
end
|
209
|
+
|
210
|
+
private
|
211
|
+
|
212
|
+
def basic_key
|
213
|
+
Base64.encode64("#{@client_id}:#{@client_secret}").gsub(/\s/, '')
|
214
|
+
end
|
215
|
+
|
216
|
+
def autorization_header
|
217
|
+
@token != nil ? "Bearer #{@token['access_token']}" : "Basic #{basic_key}"
|
218
|
+
end
|
219
|
+
|
220
|
+
def headers
|
221
|
+
user_agent_header = "ringcentral/ringcentral-ruby Ruby #{RUBY_VERSION} #{RUBY_PLATFORM}"
|
222
|
+
{
|
223
|
+
'Authorization': autorization_header,
|
224
|
+
'X-User-Agent': user_agent_header,
|
225
|
+
}
|
226
|
+
end
|
227
|
+
end
|
data/lib/subscription.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
require 'faye/websocket'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'eventmachine'
|
5
|
+
|
6
|
+
class WS
|
7
|
+
def initialize(ringcentral, events, callback, debugMode = false)
|
8
|
+
@rc = ringcentral
|
9
|
+
@events = events
|
10
|
+
@callback = callback
|
11
|
+
@debugMode = debugMode
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_ws_closed=(callback)
|
15
|
+
@on_ws_closed = callback
|
16
|
+
end
|
17
|
+
|
18
|
+
def subscribe
|
19
|
+
r = @rc.post('/restapi/oauth/wstoken').body
|
20
|
+
@t = Thread.new do
|
21
|
+
EM.run {
|
22
|
+
@ws = Faye::WebSocket::Client.new(r['uri'] + '?access_token=' + r['ws_access_token'])
|
23
|
+
if @debugMode
|
24
|
+
class << @ws
|
25
|
+
def send(message)
|
26
|
+
puts "Sending...\n" + message
|
27
|
+
super(message)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@ws.on :open do
|
32
|
+
@ws.send([
|
33
|
+
{ type: 'ClientRequest', method: 'POST', path: '/restapi/v1.0/subscription', messageId: SecureRandom.uuid },
|
34
|
+
{ deliveryMode: { transportType: 'WebSocket' }, eventFilters: @events }
|
35
|
+
].to_json())
|
36
|
+
|
37
|
+
# send a heartbeat every 10 minutes
|
38
|
+
@task = Concurrent::TimerTask.new(execution_interval: 600) do
|
39
|
+
@ws.send([
|
40
|
+
{ type: 'Heartbeat', messageId: SecureRandom.uuid },
|
41
|
+
].to_json())
|
42
|
+
end
|
43
|
+
@task.execute
|
44
|
+
end
|
45
|
+
@ws.on :message do |event|
|
46
|
+
if @debugMode
|
47
|
+
puts "Receiving...\n" + event.data
|
48
|
+
end
|
49
|
+
header, body = JSON.parse(event.data)
|
50
|
+
if header['type'] == 'ServerNotification'
|
51
|
+
@callback.call(body)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
@ws.on :close do |event|
|
55
|
+
if @on_ws_closed
|
56
|
+
@on_ws_closed.call(event)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def revoke
|
64
|
+
@t.kill
|
65
|
+
end
|
66
|
+
end
|
data/spec/fax_spec.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'dotenv'
|
2
|
+
require 'ringcentral'
|
3
|
+
require "simplecov"
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
RSpec.describe 'Fax' do
|
7
|
+
describe 'send fax' do
|
8
|
+
it 'should send a fax' do
|
9
|
+
Dotenv.load
|
10
|
+
rc = RingCentral.new(ENV['RINGCENTRAL_CLIENT_ID'], ENV['RINGCENTRAL_CLIENT_SECRET'], ENV['RINGCENTRAL_SERVER_URL'])
|
11
|
+
rc.authorize(jwt: ENV['RINGCENTRAL_JWT_TOKEN'])
|
12
|
+
r = rc.post('/restapi/v1.0/account/~/extension/~/fax',
|
13
|
+
payload: { to: [{ phoneNumber: ENV['RINGCENTRAL_RECEIVER'] }] },
|
14
|
+
files: [
|
15
|
+
['spec/test.txt', 'text/plain'],
|
16
|
+
['spec/test.png', 'image/png']
|
17
|
+
]
|
18
|
+
)
|
19
|
+
expect(r).not_to be_nil
|
20
|
+
message = r.body
|
21
|
+
expect('Fax').to eq(message['type'])
|
22
|
+
|
23
|
+
rc.revoke()
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/spec/mms_spec.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'dotenv'
|
2
|
+
require 'ringcentral'
|
3
|
+
require "simplecov"
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
RSpec.describe 'MMS' do
|
7
|
+
describe 'send MMS' do
|
8
|
+
it 'should send an MMS' do
|
9
|
+
Dotenv.load
|
10
|
+
rc = RingCentral.new(ENV['RINGCENTRAL_CLIENT_ID'], ENV['RINGCENTRAL_CLIENT_SECRET'], ENV['RINGCENTRAL_SERVER_URL'])
|
11
|
+
rc.authorize(jwt: ENV['RINGCENTRAL_JWT_TOKEN'])
|
12
|
+
|
13
|
+
r = rc.post('/restapi/v1.0/account/~/extension/~/sms',
|
14
|
+
payload: {
|
15
|
+
to: [{ phoneNumber: ENV['RINGCENTRAL_RECEIVER'] }],
|
16
|
+
from: { phoneNumber: ENV['RINGCENTRAL_SENDER'] },
|
17
|
+
text: 'hello world'
|
18
|
+
},
|
19
|
+
files: [
|
20
|
+
['spec/test.png', 'image/png']
|
21
|
+
]
|
22
|
+
)
|
23
|
+
expect(r).not_to be_nil
|
24
|
+
message = r.body
|
25
|
+
expect('SMS').to eq(message['type'])
|
26
|
+
|
27
|
+
rc.revoke()
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'dotenv'
|
2
|
+
require 'ringcentral'
|
3
|
+
require "simplecov"
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
RSpec.describe 'query params' do
|
7
|
+
describe 'single' do
|
8
|
+
it 'contain single query param' do
|
9
|
+
Dotenv.load
|
10
|
+
rc = RingCentral.new(ENV['RINGCENTRAL_CLIENT_ID'], ENV['RINGCENTRAL_CLIENT_SECRET'], ENV['RINGCENTRAL_SERVER_URL'])
|
11
|
+
rc.authorize(jwt: ENV['RINGCENTRAL_JWT_TOKEN'])
|
12
|
+
r = rc.get('/restapi/v1.0/account/~/extension/~/address-book/contact', { phoneNumber: '666' })
|
13
|
+
expect(r).not_to be_nil
|
14
|
+
message = r.body
|
15
|
+
expect(message['uri']).to include('phoneNumber=666')
|
16
|
+
|
17
|
+
r = rc.get('/restapi/v1.0/account/~/extension/~/address-book/contact', { phoneNumber: ['666', '888'] })
|
18
|
+
expect(r).not_to be_nil
|
19
|
+
message = r.body
|
20
|
+
expect(message['uri']).to include('phoneNumber=666&phoneNumber=888')
|
21
|
+
|
22
|
+
rc.revoke()
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'ringcentral'
|
2
|
+
require "simplecov"
|
3
|
+
SimpleCov.start
|
4
|
+
|
5
|
+
RSpec.describe 'RingCentral' do
|
6
|
+
describe 'ringcentral' do
|
7
|
+
it 'test_class_variables' do
|
8
|
+
expect('https://platform.ringcentral.com').to eq(RingCentral.PRODUCTION_SERVER)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'test_initializer' do
|
12
|
+
rc = RingCentral.new('client_id', 'client_secret', RingCentral.PRODUCTION_SERVER)
|
13
|
+
expect('client_id').to eq(rc.client_id)
|
14
|
+
expect('client_secret').to eq(rc.client_secret)
|
15
|
+
expect('https://platform.ringcentral.com').to eq(rc.server)
|
16
|
+
expect(false).to eq(rc.auto_refresh)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'test_authorize_uri' do
|
20
|
+
rc = RingCentral.new('client_id', 'client_secret', RingCentral.PRODUCTION_SERVER)
|
21
|
+
expect(RingCentral.PRODUCTION_SERVER + '/restapi/oauth/authorize?client_id=client_id&redirect_uri=https%3A%2F%2Fexample.com&response_type=code&state=mystate').to eq(rc.authorize_uri('https://example.com', {state: 'mystate'}))
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'test_jwt_flow' do
|
25
|
+
Dotenv.load
|
26
|
+
rc = RingCentral.new(ENV['RINGCENTRAL_CLIENT_ID'], ENV['RINGCENTRAL_CLIENT_SECRET'], ENV['RINGCENTRAL_SERVER_URL'])
|
27
|
+
expect(rc.token).to be_nil
|
28
|
+
|
29
|
+
# create token
|
30
|
+
rc.authorize(jwt: ENV['RINGCENTRAL_JWT_TOKEN'])
|
31
|
+
expect(rc.token).not_to be_nil
|
32
|
+
|
33
|
+
# refresh token
|
34
|
+
rc.refresh
|
35
|
+
expect(rc.token).not_to be_nil
|
36
|
+
|
37
|
+
# revoke token
|
38
|
+
rc.revoke
|
39
|
+
expect(rc.token).to be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'test_http_methods' do
|
43
|
+
Dotenv.load
|
44
|
+
rc = RingCentral.new(ENV['RINGCENTRAL_CLIENT_ID'], ENV['RINGCENTRAL_CLIENT_SECRET'], ENV['RINGCENTRAL_SERVER_URL'])
|
45
|
+
rc.authorize(jwt: ENV['RINGCENTRAL_JWT_TOKEN'])
|
46
|
+
|
47
|
+
# get
|
48
|
+
r = rc.get('/restapi/v1.0/account/~/extension/~')
|
49
|
+
expect(r).not_to be_nil
|
50
|
+
expect(r.body['extensionNumber']).not_to be_nil
|
51
|
+
|
52
|
+
# post
|
53
|
+
r = rc.post('/restapi/v1.0/account/~/extension/~/company-pager', payload: {
|
54
|
+
to: [{extensionId: rc.token['owner_id']}],
|
55
|
+
from: {extensionId: rc.token['owner_id']},
|
56
|
+
text: 'Hello world'
|
57
|
+
})
|
58
|
+
expect(r).not_to be_nil
|
59
|
+
message = r.body
|
60
|
+
expect('Pager').to eq(message['type'])
|
61
|
+
messageUrl = "/restapi/v1.0/account/~/extension/~/message-store/#{message['id']}"
|
62
|
+
|
63
|
+
# put
|
64
|
+
r = rc.put(messageUrl, payload: { readStatus: 'Unread' })
|
65
|
+
expect(r).not_to be_nil
|
66
|
+
message = r.body
|
67
|
+
expect('Unread').to eq(message['readStatus'])
|
68
|
+
r = rc.put(messageUrl, payload: { readStatus: 'Read' })
|
69
|
+
expect(r).not_to be_nil
|
70
|
+
message = r.body
|
71
|
+
expect('Read').to eq(message['readStatus'])
|
72
|
+
|
73
|
+
# todo: test patch
|
74
|
+
|
75
|
+
# delete
|
76
|
+
r = rc.delete(messageUrl)
|
77
|
+
expect(r).not_to be_nil
|
78
|
+
r = rc.get(messageUrl)
|
79
|
+
expect(r).not_to be_nil
|
80
|
+
message = r.body
|
81
|
+
expect('Deleted').to eq(message['availability'])
|
82
|
+
|
83
|
+
rc.revoke()
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'ringcentral'
|
2
|
+
require 'subscription'
|
3
|
+
require 'dotenv'
|
4
|
+
require 'rspec'
|
5
|
+
require "simplecov"
|
6
|
+
SimpleCov.start
|
7
|
+
|
8
|
+
Dotenv.load
|
9
|
+
$rc = RingCentral.new(ENV['RINGCENTRAL_CLIENT_ID'], ENV['RINGCENTRAL_CLIENT_SECRET'], ENV['RINGCENTRAL_SERVER_URL'])
|
10
|
+
|
11
|
+
RSpec.describe 'WebSocket Subscription' do
|
12
|
+
def createSubscription(callback)
|
13
|
+
events = [
|
14
|
+
'/restapi/v1.0/account/~/extension/~/message-store?type=Pager',
|
15
|
+
]
|
16
|
+
subscription = WS.new($rc, events, lambda { |message|
|
17
|
+
callback.call(message)
|
18
|
+
})
|
19
|
+
subscription.subscribe()
|
20
|
+
# subscription.on_ws_closed = lambda { |event|
|
21
|
+
# puts 'WebSocket closed'
|
22
|
+
# }
|
23
|
+
return subscription
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'WebSocket Subscription' do
|
27
|
+
it 'receives message notification' do
|
28
|
+
$rc.authorize(jwt: ENV['RINGCENTRAL_JWT_TOKEN'])
|
29
|
+
count = 0
|
30
|
+
sub = createSubscription(lambda { |message|
|
31
|
+
count += 1
|
32
|
+
})
|
33
|
+
|
34
|
+
$rc.post('/restapi/v1.0/account/~/extension/~/company-pager', payload: {
|
35
|
+
to: [{extensionId: $rc.token['owner_id']}],
|
36
|
+
from: {extensionId: $rc.token['owner_id']},
|
37
|
+
text: 'Hello world'
|
38
|
+
})
|
39
|
+
sleep(10)
|
40
|
+
expect(count).to be > 0
|
41
|
+
|
42
|
+
# sleep for some time and see if the websocket is still alive
|
43
|
+
sleep(20)
|
44
|
+
$rc.post('/restapi/v1.0/account/~/extension/~/company-pager', payload: {
|
45
|
+
to: [{extensionId: $rc.token['owner_id']}],
|
46
|
+
from: {extensionId: $rc.token['owner_id']},
|
47
|
+
text: 'Hello world'
|
48
|
+
})
|
49
|
+
sleep(10)
|
50
|
+
expect(count).to be > 1
|
51
|
+
|
52
|
+
sub.revoke()
|
53
|
+
$rc.revoke()
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'revoke' do
|
57
|
+
$rc.authorize(jwt: ENV['RINGCENTRAL_JWT_TOKEN'])
|
58
|
+
count = 0
|
59
|
+
subscription = createSubscription(lambda { |message|
|
60
|
+
count += 1
|
61
|
+
})
|
62
|
+
|
63
|
+
subscription.revoke()
|
64
|
+
|
65
|
+
$rc.post('/restapi/v1.0/account/~/extension/~/company-pager', payload: {
|
66
|
+
to: [{extensionId: $rc.token['owner_id']}],
|
67
|
+
from: {extensionId: $rc.token['owner_id']},
|
68
|
+
text: 'Hello world'
|
69
|
+
})
|
70
|
+
sleep(10)
|
71
|
+
|
72
|
+
expect(count).to eq(0)
|
73
|
+
$rc.revoke()
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/test-rc-sdk.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.name = 'test-rc-sdk'
|
3
|
+
gem.version = '1.0.0'
|
4
|
+
gem.authors = ['Sushil Mall']
|
5
|
+
gem.email = ['sushil.mall@ringcentral.com']
|
6
|
+
gem.description = 'Ruby SDK for you to access RingCentral platform API.'
|
7
|
+
gem.summary = 'RingCentral Ruby SDK.'
|
8
|
+
gem.homepage = 'https://github.com/SushilMallRC/ringcentral-ruby'
|
9
|
+
gem.license = 'MIT'
|
10
|
+
|
11
|
+
gem.require_paths = ['lib']
|
12
|
+
gem.files = %w(README.md test-rc-sdk.gemspec)
|
13
|
+
gem.files += Dir['lib/**/*.rb']
|
14
|
+
gem.test_files = Dir['spec/**/*.rb']
|
15
|
+
|
16
|
+
gem.add_dependency('addressable', '~> 2.8', '>= 2.8.6')
|
17
|
+
gem.add_dependency('concurrent-ruby', '~> 1.2', '>= 1.2.3')
|
18
|
+
gem.add_dependency('faraday', '~> 2.9', '>= 2.9.0')
|
19
|
+
gem.add_dependency('faraday-multipart', '~> 1.0', '>= 1.0.4')
|
20
|
+
gem.add_dependency('faye-websocket', '~> 0.11', '>= 0.11.3')
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: test-rc-sdk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sushil Mall
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-05-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: addressable
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.8'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.8.6
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.8'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.8.6
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: concurrent-ruby
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.2'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.2.3
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.2'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.2.3
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: faraday
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '2.9'
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 2.9.0
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '2.9'
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 2.9.0
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: faraday-multipart
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '1.0'
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.0.4
|
83
|
+
type: :runtime
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 1.0.4
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: faye-websocket
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0.11'
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 0.11.3
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.11'
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 0.11.3
|
113
|
+
description: Ruby SDK for you to access RingCentral platform API.
|
114
|
+
email:
|
115
|
+
- sushil.mall@ringcentral.com
|
116
|
+
executables: []
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- README.md
|
121
|
+
- lib/ringcentral.rb
|
122
|
+
- lib/subscription.rb
|
123
|
+
- spec/fax_spec.rb
|
124
|
+
- spec/mms_spec.rb
|
125
|
+
- spec/query_params_spec.rb
|
126
|
+
- spec/ringcentral_spec.rb
|
127
|
+
- spec/websocket_subscription_spec.rb
|
128
|
+
- test-rc-sdk.gemspec
|
129
|
+
homepage: https://github.com/SushilMallRC/ringcentral-ruby
|
130
|
+
licenses:
|
131
|
+
- MIT
|
132
|
+
metadata: {}
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
requirements: []
|
148
|
+
rubygems_version: 3.4.19
|
149
|
+
signing_key:
|
150
|
+
specification_version: 4
|
151
|
+
summary: RingCentral Ruby SDK.
|
152
|
+
test_files:
|
153
|
+
- spec/fax_spec.rb
|
154
|
+
- spec/mms_spec.rb
|
155
|
+
- spec/query_params_spec.rb
|
156
|
+
- spec/ringcentral_spec.rb
|
157
|
+
- spec/websocket_subscription_spec.rb
|