test-rc-sdk 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Ruby](https://github.com/ringcentral/ringcentral-ruby/actions/workflows/ruby.yml/badge.svg)](https://github.com/ringcentral/ringcentral-ruby/actions/workflows/ruby.yml)
|
4
|
+
[![Reference](https://img.shields.io/badge/rubydoc-reference-blue?logo=ruby)](https://ringcentral.github.io/ringcentral-ruby/)
|
5
|
+
[![Twitter](https://img.shields.io/twitter/follow/ringcentraldevs.svg?style=social&label=follow)](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
|