stream-chat-ruby 2.4.0 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea550365bc302aeb9097c2f2819a0dd6c5ead1fd1064a7c12842de4670e871d4
4
- data.tar.gz: b01a528a334338edfd9f05e8f3aa40e682cabd3de40e6356b162f9f0cc791e64
3
+ metadata.gz: e9f49f20f526f3ddd42becd925d877020fea2b4afdca80327a1d31beeafcdd76
4
+ data.tar.gz: d38e3c3390338c96f30ff692459aebd0c54addab98071130e089b9aed9a5ec13
5
5
  SHA512:
6
- metadata.gz: 3f12aa7bec1853b9a91e3b9a63a6f296aca373bb8f24a719a0728959e68cbe10e4b440bc7056d730bfa36f3e2a0c5c1b4db1b566cc51159a09bad3eac4694747
7
- data.tar.gz: 9982645b54641b15fd7d8deae0049d90aca66defcaba509e19be7f0c27dfd42fec8bba5221b0ca961251fc8c084c61a1793fcc689df853ae09a570b0358ac605
6
+ metadata.gz: a58994964df76b94ef804b25372acdf6cfe2d142f32fdfed54be0bcff81d5203ec5d8e38e5c0a0f216e43b2ba4713a35e722fa8eff92204f95e458a5ce3644a7
7
+ data.tar.gz: 721e797d9968d088e90578548794457812e483c50ff12d138fbbbda37af3f5328d7290fb97103ea82b164462182a45a18bb64185c1d0df27e06cd3e6064befdf
@@ -6,9 +6,10 @@ jobs:
6
6
  build:
7
7
  runs-on: ubuntu-latest
8
8
  strategy:
9
+ max-parallel: 1
9
10
  matrix:
10
- ruby: [ '2.5', '2.6', '2.7' ]
11
- name: Ruby ${{ matrix.ruby }} sample
11
+ ruby: ['2.5', '2.6', '2.7', '3.0']
12
+ name: Ruby ${{ matrix.ruby }}
12
13
  steps:
13
14
  - uses: actions/checkout@v2
14
15
  - uses: actions/setup-ruby@v1
data/.gitignore CHANGED
@@ -48,3 +48,4 @@ Gemfile.lock
48
48
 
49
49
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
50
  .rvmrc
51
+ .envrc
data/CHANGELOG.md CHANGED
@@ -1,12 +1,42 @@
1
+ ## June 4th, 2021 - 2.10.0
2
+
3
+ - Add custom command CRUD support
4
+
5
+ ## May 31st, 2021 - 2.9.0
6
+
7
+ - Add support for app and user level token revoke
8
+
9
+ ## May 21st, 2021 - 2.8.0
10
+
11
+ - Add query message flags support
12
+
13
+ ## March 17th, 2021 - 2.7.0
14
+
15
+ - Add Ruby 3.x support
16
+ - Update CI to run all tests for all versions
17
+
18
+ ## March 9th, 2021 - 2.6.0
19
+
20
+ - Add get_rate_limits endpoint
21
+
22
+ ## February 3rd, 2021 - 2.5.0
23
+
24
+ - Add channel partial update
25
+ - Increase convenience in query members
26
+ - Improve internal symbol conversions
27
+
1
28
  ## January 20th, 2021 - 2.4.0
29
+
2
30
  - Add query_members to channel
3
31
  - Use post endpoint for query channels instead of get
4
32
  - Extract common code for sorting into a helper for query calls
5
33
 
6
34
  ## January 5th, 2021 - 2.3.0
35
+
7
36
  - Add check SQS helper
8
37
 
9
38
  ## January 4th, 2021 - 2.2.0
39
+
10
40
  - Add support for export channels
11
41
  - Improve readme for blocklist and export channels
12
42
  - Improve running tests for multiple versions of ruby
@@ -14,27 +44,34 @@
14
44
  - Move to GitHub Actions
15
45
 
16
46
  ## October 5th, 2020 - 2.1.0
47
+
17
48
  - Add support for blocklist
18
49
 
19
50
  ## October 2nd, 2020 - 2.0.0
51
+
20
52
  - Drop EOL Ruby versions: 2.3 && 2.4
21
53
  - Setup Rubocop and mark string literals as frozen
22
54
 
23
55
  ## August 3rd, 2020 - 1.1.3
56
+
24
57
  - Fixed Argument Error on delete_user
25
58
 
26
59
  ## April 23th, 2020 - 1.1.2
60
+
27
61
  - Fixed ArgumentError when no users was passed
28
62
 
29
63
  ## March 30th, 2020 - 1.1.1
64
+
30
65
  - Fixed few minor issues
31
66
 
32
67
  ## Oct 27th, 2019 - 1.1.0
68
+
33
69
  - Mark gems use for testing as development dependencies
34
70
  - Added `send_file`, `send_image`, `delete_file`, `delete_image`
35
71
  - Added `invite_members`
36
72
 
37
73
  ## Oct 19th, 2019 - 1.0.0
74
+
38
75
  - Added `channel.hide` and `channel.show`
39
76
  - Added `client.flag_message` and `client.unflag_message`
40
77
  - Added `client.flag_user` and `client.unflag_user`
data/README.md CHANGED
@@ -1,20 +1,20 @@
1
1
  # stream-chat-ruby
2
2
 
3
- [![build](https://github.com/GetStream/stream-chat-ruby/workflows/build/badge.svg)](https://github.com/GetStream/stream-chat-ruby/actions) [![Gem Version](https://badge.fury.io/rb/stream-chat-ruby.svg)](http://badge.fury.io/rb/stream-chat-ruby)
3
+ [![build](https://github.com/GetStream/stream-chat-ruby/workflows/build/badge.svg)](https://github.com/GetStream/stream-chat-ruby/actions) [![Gem Version](https://badge.fury.io/rb/stream-chat-ruby.svg)](http://badge.fury.io/rb/stream-chat-ruby)
4
4
 
5
5
  stream-chat-ruby is the official Ruby client for [Stream chat](https://getstream.io/chat/) a service for building chat applications.
6
6
 
7
7
  You can sign up for a Stream account at https://getstream.io/chat/get_started/.
8
8
 
9
9
  You can use this library to access chat API endpoints server-side. For the
10
- client-side integrations (web and mobile) have a look at the Javascript, iOS and
10
+ client-side integrations (web and mobile) have a look at the JavaScript, iOS and
11
11
  Android SDK libraries (https://getstream.io/chat/).
12
12
 
13
13
  ### Installation
14
14
 
15
15
  stream-chat-ruby supports:
16
16
 
17
- - Ruby (2.7, 2.6, 2.5)
17
+ - Ruby (3.0, 2.7, 2.6, 2.5)
18
18
 
19
19
  #### Install
20
20
 
@@ -131,8 +131,16 @@ chan.unban_user('bob-1')
131
131
  # Query channel state
132
132
  chan.query({'messages' => { 'limit' => 10, 'id_lte' => m1['id']}})
133
133
 
134
+ # Update metadata (overwrite)
135
+ chan.update({'motd' => 'one apple a day....'})
136
+
137
+ # Update partial
138
+ # 1. key-value pairs to set
139
+ # 2. keys to unset (remove)
140
+ chan.update_partial({color: 'blue', age: 30}, ['motd'])
141
+
134
142
  # Query channel members
135
- chan.query_members({name: {'$autocomplete': 'test'}}, {last_created_at: -1}, offset: 5, limit: 5)
143
+ chan.query_members({name: {'$autocomplete': 'test'}}, sort: {last_created_at: -1}, offset: 5, limit: 5)
136
144
  ```
137
145
 
138
146
  ### Messages
@@ -157,6 +165,7 @@ client.remove_device(jane_phone['id'], jane_phone['user_id'])
157
165
  ```
158
166
 
159
167
  ### Blocklists
168
+
160
169
  ```ruby
161
170
  # Create a blocklist
162
171
  client.create_blocklist('my_blocker', %w[fudge cream sugar])
@@ -172,6 +181,7 @@ client.delete_blocklist('my_blocker')
172
181
  ```
173
182
 
174
183
  ### Export Channels
184
+
175
185
  ```ruby
176
186
  # Register an export
177
187
  response = client.export_channels({type: 'messaging', id: 'jane'})
@@ -181,6 +191,19 @@ status_response = client.get_export_channel_status(response['task_id'])
181
191
  # status_response['status'] == 'pending', 'completed'
182
192
  ```
183
193
 
194
+ ### Rate limits
195
+
196
+ ```ruby
197
+ # Get all rate limits
198
+ limits = client.get_rate_limits
199
+
200
+ # Get rate limits for specific platform(s)
201
+ limits = client.get_rate_limits(server_side: true)
202
+
203
+ # Get rate limits for specific platforms and endpoints
204
+ limits = client.get_rate_limits(android: true, ios: true, endpoints: ['QueryChannels', 'SendMessage'])
205
+ ```
206
+
184
207
  ### Example Rails application
185
208
 
186
209
  See [an example rails application using the Ruby SDK](https://github.com/GetStream/rails-chat-example).
@@ -25,7 +25,7 @@ module StreamChat
25
25
  end
26
26
 
27
27
  def send_message(message, user_id)
28
- payload = { "message": add_user_id(message, user_id) }
28
+ payload = { message: add_user_id(message, user_id) }
29
29
  @client.post("#{url}/message", data: payload)
30
30
  end
31
31
 
@@ -35,24 +35,24 @@ module StreamChat
35
35
  end
36
36
 
37
37
  def send_reaction(message_id, reaction, user_id)
38
- payload = { "reaction": add_user_id(reaction, user_id) }
38
+ payload = { reaction: add_user_id(reaction, user_id) }
39
39
  @client.post("messages/#{message_id}/reaction", data: payload)
40
40
  end
41
41
 
42
42
  def delete_reaction(message_id, reaction_type, user_id)
43
43
  @client.delete(
44
44
  "messages/#{message_id}/reaction/#{reaction_type}",
45
- params: { "user_id": user_id }
45
+ params: { user_id: user_id }
46
46
  )
47
47
  end
48
48
 
49
49
  def create(user_id)
50
- @custom_data['created_by'] = { "id": user_id }
50
+ @custom_data['created_by'] = { id: user_id }
51
51
  query(watch: false, state: false, presence: false)
52
52
  end
53
53
 
54
54
  def query(**options)
55
- payload = { "state": true, "data": @custom_data }.merge(options)
55
+ payload = { state: true, data: @custom_data }.merge(options)
56
56
  url = "channels/#{@channel_type}"
57
57
  url = "#{url}/#{@id}" unless @id.nil?
58
58
 
@@ -61,7 +61,7 @@ module StreamChat
61
61
  state
62
62
  end
63
63
 
64
- def query_members(filter_conditions: {}, sort: nil, **options)
64
+ def query_members(filter_conditions = {}, sort: nil, **options)
65
65
  params = {}.merge(options).merge({
66
66
  id: @id,
67
67
  type: @channel_type,
@@ -80,10 +80,17 @@ module StreamChat
80
80
  end
81
81
 
82
82
  def update(channel_data, update_message = nil)
83
- payload = { "data": channel_data, "message": update_message }
83
+ payload = { data: channel_data, message: update_message }
84
84
  @client.post(url, data: payload)
85
85
  end
86
86
 
87
+ def update_partial(set = nil, unset = nil)
88
+ raise StreamChannelException 'set or unset is needed' if set.nil? && unset.nil?
89
+
90
+ payload = { set: set, unset: unset }
91
+ @client.patch(url, data: payload)
92
+ end
93
+
87
94
  def delete
88
95
  @client.delete(url)
89
96
  end
@@ -93,23 +100,23 @@ module StreamChat
93
100
  end
94
101
 
95
102
  def add_members(user_ids)
96
- @client.post(url, data: { "add_members": user_ids })
103
+ @client.post(url, data: { add_members: user_ids })
97
104
  end
98
105
 
99
106
  def invite_members(user_ids)
100
- @client.post(url, data: { "invites": user_ids })
107
+ @client.post(url, data: { invites: user_ids })
101
108
  end
102
109
 
103
110
  def add_moderators(user_ids)
104
- @client.post(url, data: { "add_moderators": user_ids })
111
+ @client.post(url, data: { add_moderators: user_ids })
105
112
  end
106
113
 
107
114
  def remove_members(user_ids)
108
- @client.post(url, data: { "remove_members": user_ids })
115
+ @client.post(url, data: { remove_members: user_ids })
109
116
  end
110
117
 
111
118
  def demote_moderators(user_ids)
112
- @client.post(url, data: { "demote_moderators": user_ids })
119
+ @client.post(url, data: { demote_moderators: user_ids })
113
120
  end
114
121
 
115
122
  def mark_read(user_id, **options)
@@ -150,17 +157,17 @@ module StreamChat
150
157
  end
151
158
 
152
159
  def delete_file(url)
153
- @client.delete("#{self.url}/file", params: { "url": url })
160
+ @client.delete("#{self.url}/file", params: { url: url })
154
161
  end
155
162
 
156
163
  def delete_image(url)
157
- @client.delete("#{self.url}/image", params: { "url": url })
164
+ @client.delete("#{self.url}/image", params: { url: url })
158
165
  end
159
166
 
160
167
  private
161
168
 
162
169
  def add_user_id(payload, user_id)
163
- payload.merge({ "user": { "id": user_id } })
170
+ payload.merge({ user: { id: user_id } })
164
171
  end
165
172
  end
166
173
  end
@@ -4,6 +4,7 @@
4
4
  require 'open-uri'
5
5
  require 'faraday'
6
6
  require 'jwt'
7
+ require 'time'
7
8
  require 'stream-chat/channel'
8
9
  require 'stream-chat/errors'
9
10
  require 'stream-chat/version'
@@ -45,9 +46,10 @@ module StreamChat
45
46
  end
46
47
  end
47
48
 
48
- def create_token(user_id, exp = nil)
49
+ def create_token(user_id, exp = nil, iat = nil)
49
50
  payload = { user_id: user_id }
50
51
  payload['exp'] = exp unless exp.nil?
52
+ payload['iat'] = iat unless iat.nil?
51
53
  JWT.encode(payload, @api_secret, 'HS256')
52
54
  end
53
55
 
@@ -60,22 +62,29 @@ module StreamChat
60
62
  end
61
63
 
62
64
  def flag_message(id, **options)
63
- payload = { 'target_message_id': id }.merge(options)
65
+ payload = { target_message_id: id }.merge(options)
64
66
  post('moderation/flag', data: payload)
65
67
  end
66
68
 
67
69
  def unflag_message(id, **options)
68
- payload = { 'target_message_id': id }.merge(options)
70
+ payload = { target_message_id: id }.merge(options)
69
71
  post('moderation/unflag', data: payload)
70
72
  end
71
73
 
74
+ def query_message_flags(filter_conditions, **options)
75
+ params = options.merge({
76
+ filter_conditions: filter_conditions
77
+ })
78
+ get('moderation/flags/message', params: { payload: params.to_json })
79
+ end
80
+
72
81
  def flag_user(id, **options)
73
- payload = { 'target_user_id': id }.merge(options)
82
+ payload = { target_user_id: id }.merge(options)
74
83
  post('moderation/flag', data: payload)
75
84
  end
76
85
 
77
86
  def unflag_user(id, **options)
78
- payload = { 'target_user_id': id }.merge(options)
87
+ payload = { target_user_id: id }.merge(options)
79
88
  post('moderation/unflag', data: payload)
80
89
  end
81
90
 
@@ -85,11 +94,11 @@ module StreamChat
85
94
 
86
95
  def search(filter_conditions, query, **options)
87
96
  params = options.merge({
88
- "filter_conditions": filter_conditions,
89
- "query": query
97
+ filter_conditions: filter_conditions,
98
+ query: query
90
99
  })
91
100
 
92
- get('search', params: { "payload": params.to_json })
101
+ get('search', params: { payload: params.to_json })
93
102
  end
94
103
 
95
104
  def update_users(users)
@@ -100,7 +109,7 @@ module StreamChat
100
109
 
101
110
  payload[id] = user
102
111
  end
103
- post('users', data: { 'users': payload })
112
+ post('users', data: { users: payload })
104
113
  end
105
114
 
106
115
  def update_user(user)
@@ -108,7 +117,7 @@ module StreamChat
108
117
  end
109
118
 
110
119
  def update_users_partial(updates)
111
- patch('users', data: { 'users': updates })
120
+ patch('users', data: { users: updates })
112
121
  end
113
122
 
114
123
  def update_user_partial(update)
@@ -132,34 +141,34 @@ module StreamChat
132
141
  end
133
142
 
134
143
  def ban_user(target_id, **options)
135
- payload = { 'target_user_id': target_id }.merge(options)
144
+ payload = { target_user_id: target_id }.merge(options)
136
145
  post('moderation/ban', data: payload)
137
146
  end
138
147
 
139
148
  def unban_user(target_id, **options)
140
- params = { 'target_user_id': target_id }.merge(options)
149
+ params = { target_user_id: target_id }.merge(options)
141
150
  delete('moderation/ban', params: params)
142
151
  end
143
152
 
144
153
  def mute_user(target_id, user_id)
145
- payload = { 'target_id': target_id, 'user_id': user_id }
154
+ payload = { target_id: target_id, user_id: user_id }
146
155
  post('moderation/mute', data: payload)
147
156
  end
148
157
 
149
158
  def unmute_user(target_id, user_id)
150
- payload = { 'target_id': target_id, 'user_id': user_id }
159
+ payload = { target_id: target_id, user_id: user_id }
151
160
  post('moderation/unmute', data: payload)
152
161
  end
153
162
 
154
163
  def mark_all_read(user_id)
155
- payload = { 'user': { 'id': user_id } }
164
+ payload = { user: { id: user_id } }
156
165
  post('channels/read', data: payload)
157
166
  end
158
167
 
159
168
  def update_message(message)
160
169
  raise ArgumentError 'message must have an id' unless message.key? 'id'
161
170
 
162
- post("messages/#{message['id']}", data: { 'message': message })
171
+ post("messages/#{message['id']}", data: { message: message })
163
172
  end
164
173
 
165
174
  def delete_message(message_id)
@@ -168,14 +177,14 @@ module StreamChat
168
177
 
169
178
  def query_users(filter_conditions, sort: nil, **options)
170
179
  params = options.merge({
171
- "filter_conditions": filter_conditions,
172
- "sort": get_sort_fields(sort)
180
+ filter_conditions: filter_conditions,
181
+ sort: get_sort_fields(sort)
173
182
  })
174
- get('users', params: { "payload": params.to_json })
183
+ get('users', params: { payload: params.to_json })
175
184
  end
176
185
 
177
186
  def query_channels(filter_conditions, sort: nil, **options)
178
- data = { "state": true, "watch": false, "presence": false }
187
+ data = { state: true, watch: false, presence: false }
179
188
  data = data.merge(options).merge({
180
189
  filter_conditions: filter_conditions,
181
190
  sort: get_sort_fields(sort)
@@ -218,18 +227,29 @@ module StreamChat
218
227
 
219
228
  def add_device(device_id, push_provider, user_id)
220
229
  post('devices', data: {
221
- "id": device_id,
222
- "push_provider": push_provider,
223
- "user_id": user_id
230
+ id: device_id,
231
+ push_provider: push_provider,
232
+ user_id: user_id
224
233
  })
225
234
  end
226
235
 
227
236
  def delete_device(device_id, user_id)
228
- delete('devices', params: { "id": device_id, "user_id": user_id })
237
+ delete('devices', params: { id: device_id, user_id: user_id })
229
238
  end
230
239
 
231
240
  def get_devices(user_id)
232
- get('devices', params: { "user_id": user_id })
241
+ get('devices', params: { user_id: user_id })
242
+ end
243
+
244
+ def get_rate_limits(server_side: false, android: false, ios: false, web: false, endpoints: [])
245
+ params = {}
246
+ params['server_side'] = server_side if server_side
247
+ params['android'] = android if android
248
+ params['ios'] = ios if ios
249
+ params['web'] = web if web
250
+ params['endpoints'] = endpoints.join(',') unless endpoints.empty?
251
+
252
+ get('rate_limits', params: params)
233
253
  end
234
254
 
235
255
  def verify_webhook(request_body, x_signature)
@@ -246,11 +266,11 @@ module StreamChat
246
266
  end
247
267
 
248
268
  def create_blocklist(name, words)
249
- post('blocklists', data: { "name": name, "words": words })
269
+ post('blocklists', data: { name: name, words: words })
250
270
  end
251
271
 
252
272
  def update_blocklist(name, words)
253
- put("blocklists/#{name}", data: { "words": words })
273
+ put("blocklists/#{name}", data: { words: words })
254
274
  end
255
275
 
256
276
  def delete_blocklist(name)
@@ -258,13 +278,37 @@ module StreamChat
258
278
  end
259
279
 
260
280
  def export_channels(*channels)
261
- post('export_channels', data: { "channels": channels })
281
+ post('export_channels', data: { channels: channels })
262
282
  end
263
283
 
264
284
  def get_export_channel_status(task_id)
265
285
  get("export_channels/#{task_id}")
266
286
  end
267
287
 
288
+ def revoke_tokens(before)
289
+ before = before.rfc3339 if before.instance_of?(DateTime)
290
+ update_app_settings({ 'revoke_tokens_issued_before' => before })
291
+ end
292
+
293
+ def revoke_user_token(user_id, before)
294
+ revoke_users_token([user_id], before)
295
+ end
296
+
297
+ def revoke_users_token(user_ids, before)
298
+ before = before.rfc3339 if before.instance_of?(DateTime)
299
+
300
+ updates = []
301
+ user_ids.each do |user_id|
302
+ updates.push({
303
+ 'id' => user_id,
304
+ 'set' => {
305
+ 'revoke_tokens_issued_before' => before
306
+ }
307
+ })
308
+ end
309
+ update_users_partial(updates)
310
+ end
311
+
268
312
  def put(relative_url, params: nil, data: nil)
269
313
  make_http_request(:put, relative_url, params: params, data: data)
270
314
  end
@@ -305,7 +349,27 @@ module StreamChat
305
349
  end
306
350
 
307
351
  def check_sqs(sqs_key = nil, sqs_secret = nil, sqs_url = nil)
308
- post('check_sqs', data: { "sqs_key": sqs_key, "sqs_secret": sqs_secret, "sqs_url": sqs_url })
352
+ post('check_sqs', data: { sqs_key: sqs_key, sqs_secret: sqs_secret, sqs_url: sqs_url })
353
+ end
354
+
355
+ def create_command(command)
356
+ post('commands', data: command)
357
+ end
358
+
359
+ def get_command(name)
360
+ get("commands/#{name}")
361
+ end
362
+
363
+ def update_command(name, command)
364
+ put("commands/#{name}", data: command)
365
+ end
366
+
367
+ def delete_command(name)
368
+ delete("commands/#{name}")
369
+ end
370
+
371
+ def list_commands
372
+ get('commands')
309
373
  end
310
374
 
311
375
  private
@@ -320,8 +384,8 @@ module StreamChat
320
384
 
321
385
  def get_default_headers
322
386
  {
323
- "Content-Type": 'application/json',
324
- "X-Stream-Client": get_user_agent
387
+ 'Content-Type': 'application/json',
388
+ 'X-Stream-Client': get_user_agent
325
389
  }
326
390
  end
327
391
 
@@ -342,7 +406,7 @@ module StreamChat
342
406
  headers['stream-auth-type'] = 'jwt'
343
407
  url = [@base_url, relative_url].join('/')
344
408
  params = params.nil? ? {} : params
345
- params = Hash[get_default_params.merge(params).sort_by { |k, _v| k.to_s }]
409
+ params = (get_default_params.merge(params).sort_by { |k, _v| k.to_s }).to_h
346
410
  url = "#{url}?#{URI.encode_www_form(params)}"
347
411
 
348
412
  body = data.to_json if %w[patch post put].include? method.to_s
@@ -3,5 +3,5 @@
3
3
  # lib/version.rb
4
4
 
5
5
  module StreamChat
6
- VERSION = '2.4.0'
6
+ VERSION = '2.10.0'
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stream-chat-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.0
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mircea Cosbuc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-20 00:00:00.000000000 Z
11
+ date: 2021-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday