moesif_rack 1.3.9 → 1.4.3

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: 8f5cd1d13df1da4833bc55a10462721bdabae0a09cb6d8ab6398c378bd546975
4
- data.tar.gz: ab2e62d3c6c4f76532bf8dbad38b4fdb2747413897e72228dc1ac23265d134c9
3
+ metadata.gz: e8bde32f3f60b62bfe1dfd5aef52ba6c56f21db3e2f8ac78743972c227ff1d2b
4
+ data.tar.gz: d0fe24cc62aa794dab07b04fa6ce943f85d015db045eb654cd66e9827e9b97fe
5
5
  SHA512:
6
- metadata.gz: f42082b67c965208ba8d13bb2552a2b44ee08c3930ed58825838804431bfda1655acbcd82099ea0d0baec4573e31c0db4dc91dc95a7c2e7b5f23fb401e5f7076
7
- data.tar.gz: ac4f6d15abbb9cce97e8206a158666883a002575d9921b4c64c54a3f38f384034e189c5dbb26ae44ac6636b66fb8a842fee1ad02b09e3c59b9fd9408815f813e
6
+ metadata.gz: 0db637f28ba8d87d7e5e8a81b9642eeeb64f2c701d862c941e90ac0bdc5a2d15584f597df82420b736020dcd8f426c87e1e727cb9dcf081214e9aeecc07fdcd9
7
+ data.tar.gz: 134f36829793337b6d78a7a51dedddeadd2d4e2fc1353e5b0ae5962f7ddb72f08562c5ff9f5f93248b7ee80db14a987f4935c58eb152b0229bfae9f7ea9e128c
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2019 Moesif, Inc
1
+ Copyright (c) 2020 Moesif, Inc
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
data/README.md CHANGED
@@ -22,7 +22,7 @@ gem install moesif_rack
22
22
  and if you have a `Gemfile` in your project, please add this line to
23
23
 
24
24
  ```
25
- gem 'moesif_rack', '~> 1.3.9'
25
+ gem 'moesif_rack', '~> 1.4.2'
26
26
 
27
27
  ```
28
28
 
@@ -126,12 +126,10 @@ Optional.
126
126
  identify_user is a Proc that takes env, headers, and body as arguments and returns a user_id string. This helps us attribute requests to unique users. Even though Moesif can automatically retrieve the user_id without this, this is highly recommended to ensure accurate attribution.
127
127
 
128
128
  ```ruby
129
-
130
129
  moesif_options['identify_user'] = Proc.new { |env, headers, body|
131
130
 
132
- #snip
133
-
134
- 'my_user_id'
131
+ # Add your custom code that returns a string for user id
132
+ '12345'
135
133
  }
136
134
 
137
135
  ```
@@ -145,13 +143,24 @@ identify_company is a Proc that takes env, headers, and body as arguments and re
145
143
 
146
144
  moesif_options['identify_company'] = Proc.new { |env, headers, body|
147
145
 
148
- #snip
149
-
150
- 'my_company_id'
146
+ # Add your custom code that returns a string for company id
147
+ '67890'
151
148
  }
152
149
 
153
150
  ```
154
151
 
152
+ #### __`identify_session`__
153
+
154
+ Optional. A Proc that takes env, headers, body and returns a string.
155
+
156
+ ```ruby
157
+
158
+ moesif_options['identify_session'] = Proc.new { |env, headers, body|
159
+ # Add your custom code that returns a string for session/API token
160
+ 'XXXXXXXXX'
161
+ }
162
+ ```
163
+
155
164
  #### __`get_metadata`__
156
165
 
157
166
  Optional.
@@ -162,31 +171,15 @@ metadata to this event.
162
171
  ```ruby
163
172
 
164
173
  moesif_options['get_metadata'] = Proc.new { |env, headers, body|
165
-
166
- #snip
174
+ # Add your custom code that returns a dictionary
167
175
  value = {
168
- 'foo' => 'abc',
169
- 'bar' => '123'
176
+ 'datacenter' => 'westus',
177
+ 'deployment_version' => 'v1.2.3'
170
178
  }
171
-
172
179
  value
173
180
  }
174
181
  ```
175
182
 
176
- #### __`identify_session`__
177
-
178
- Optional. A Proc that takes env, headers, body and returns a string.
179
-
180
- ```ruby
181
-
182
- moesif_options['identify_session'] = Proc.new { |env, headers, body|
183
-
184
- #snip
185
-
186
- 'the_session_token'
187
- }
188
-
189
- ```
190
183
 
191
184
  #### __`mask_data`__
192
185
 
@@ -196,9 +189,8 @@ With mask_data, you can make modifications to headers or body of the event befor
196
189
  ```ruby
197
190
 
198
191
  moesif_options['mask_data'] = Proc.new { |event_model|
199
-
200
- #snip
201
-
192
+ # Add your custom code that returns a event_model after modifying any fields
193
+ event_model.response.body.password = nil
202
194
  event_model
203
195
  }
204
196
 
@@ -213,9 +205,7 @@ Optional. A Proc that takes env, headers, body and returns a boolean.
213
205
  ```ruby
214
206
 
215
207
  moesif_options['skip'] = Proc.new { |env, headers, body|
216
-
217
- #snip
218
-
208
+ # Add your custom code that returns true to skip logging the API call
219
209
  false
220
210
  }
221
211
 
@@ -232,7 +222,7 @@ Optional. Boolean. Default false. If true, it will print out debug messages. In
232
222
 
233
223
  Optional. Boolean. Default true. If false, will not log request and response body to Moesif.
234
224
 
235
- #### __`capture_outoing_requests`__
225
+ #### __`capture_outgoing_requests`__
236
226
  Optional. boolean, Default `false`. Set to `true` to capture all outgoing API calls from your app to third parties like Stripe, Github or to your own dependencies while using [Net::HTTP](https://ruby-doc.org/stdlib-2.6.3/libdoc/net/http/rdoc/Net/HTTP.html) package. The options below is applied to outgoing API calls. When the request is outgoing, for options functions that take request and response as input arguments, the request and response objects passed in are [Request](https://www.rubydoc.info/stdlib/net/Net/HTTPRequest) request and [Response](https://www.rubydoc.info/stdlib/net/Net/HTTPResponse) response objects.
237
227
 
238
228
 
@@ -245,9 +235,8 @@ identify_user_outgoing is a Proc that takes request and response as arguments an
245
235
 
246
236
  moesif_options['identify_user_outgoing'] = Proc.new { |request, response|
247
237
 
248
- #snip
249
-
250
- 'the_user_id'
238
+ # Add your custom code that returns a string for user id
239
+ '12345'
251
240
  }
252
241
 
253
242
  ```
@@ -261,9 +250,8 @@ identify_company_outgoing is a Proc that takes request and response as arguments
261
250
 
262
251
  moesif_options['identify_company_outgoing'] = Proc.new { |request, response|
263
252
 
264
- #snip
265
-
266
- 'the_company_id'
253
+ # Add your custom code that returns a string for company id
254
+ '67890'
267
255
  }
268
256
 
269
257
  ```
@@ -279,12 +267,11 @@ metadata to this event.
279
267
 
280
268
  moesif_options['get_metadata_outgoing'] = Proc.new { |request, response|
281
269
 
282
- #snip
270
+ # Add your custom code that returns a dictionary
283
271
  value = {
284
- 'foo' => 'abc',
285
- 'bar' => '123'
272
+ 'datacenter' => 'westus',
273
+ 'deployment_version' => 'v1.2.3'
286
274
  }
287
-
288
275
  value
289
276
  }
290
277
  ```
@@ -297,9 +284,8 @@ Optional. A Proc that takes request, response and returns a string.
297
284
 
298
285
  moesif_options['identify_session_outgoing'] = Proc.new { |request, response|
299
286
 
300
- #snip
301
-
302
- 'the_session_token'
287
+ # Add your custom code that returns a string for session/API token
288
+ 'XXXXXXXXX'
303
289
  }
304
290
 
305
291
  ```
@@ -312,8 +298,7 @@ Optional. A Proc that takes request, response and returns a boolean. If `true` w
312
298
 
313
299
  moesif_options['skip_outgoing'] = Proc.new{ |request, response|
314
300
 
315
- #snip
316
-
301
+ # Add your custom code that returns true to skip logging the API call
317
302
  false
318
303
  }
319
304
 
@@ -328,8 +313,8 @@ With mask_data_outgoing, you can make modifications to headers or body of the ev
328
313
 
329
314
  moesif_options['mask_data_outgoing'] = Proc.new { |event_model|
330
315
 
331
- #snip
332
-
316
+ # Add your custom code that returns a event_model after modifying any fields
317
+ event_model.response.body.password = nil
333
318
  event_model
334
319
  }
335
320
 
@@ -341,103 +326,169 @@ Optional. Boolean. Default true. If false, will not log request and response bod
341
326
 
342
327
  ## Update User
343
328
 
344
- ### update_user method
345
- A method is attached to the moesif middleware object to update the user profile or metadata.
346
- The metadata field can be any custom data you want to set on the user. The `user_id` field is required.
329
+ ### Update a Single User
330
+ Create or update a user profile in Moesif.
331
+ The metadata field can be any customer demographic or other info you want to store.
332
+ Only the `user_id` field is required.
333
+ This method is a convenient helper that calls the Moesif API lib.
334
+ For details, visit the [Ruby API Reference](https://www.moesif.com/docs/api?ruby#update-a-user).
347
335
 
348
336
  ```ruby
349
- metadata = JSON.parse('{'\
350
- '"email": "testrubyapi@user.com",'\
351
- '"name": "ruby api user",'\
352
- '"custom": "testdata"'\
353
- '}')
354
-
355
- campaign_model = {"utm_source" => "Newsletter",
356
- "utm_medium" => "Email"}
337
+ metadata = {
338
+ :email => 'john@acmeinc.com',
339
+ :first_name => 'John',
340
+ :last_name => 'Doe',
341
+ :title => 'Software Engineer',
342
+ :salesInfo => {
343
+ :stage => 'Customer',
344
+ :lifetime_value => 24000,
345
+ :accountOwner => 'mary@contoso.com',
346
+ }
347
+ }
357
348
 
358
- user_model = { "user_id" => "12345",
359
- "company_id" => "67890",
360
- "modified_time" => Time.now.utc.iso8601,
361
- "metadata" => metadata,
362
- "campaign" => campaign_model }
349
+ # Campaign object is optional, but useful if you want to track ROI of acquisition channels
350
+ # See https://www.moesif.com/docs/api#users for campaign schema
351
+ campaign = MoesifApi::CampaignModel.new()
352
+ campaign.utm_source = "google"
353
+ campaign.utm_medium = "cpc"
354
+ campaign.utm_campaign = "adwords"
355
+ campaign.utm_term = "api+tooling"
356
+ campaign.utm_content = "landing"
357
+
358
+ # Only user_id is required.
359
+ # metadata can be any custom object
360
+ user = MoesifApi::UserModel.new()
361
+ user.user_id = "12345"
362
+ user.company_id = "67890" # If set, associate user with a company object
363
+ user.campaign = campaign
364
+ user.metadata = metadata
363
365
 
364
366
  update_user = MoesifRack::MoesifMiddleware.new(@app, @options).update_user(user_model)
365
367
  ```
366
368
 
367
- ### update_users_batch method
368
- A method is attached to the moesif middleware object to update the users profile or metadata in batch.
369
- The metadata field can be any custom data you want to set on the user. The `user_id` field is required.
369
+ ### Update Users in Batch
370
+ Similar to update_user, but used to update a list of users in one batch.
371
+ Only the `user_id` field is required.
372
+ This method is a convenient helper that calls the Moesif API lib.
373
+ For details, visit the [Ruby API Reference](https://www.moesif.com/docs/api?ruby#update-users-in-batch).
370
374
 
371
375
  ```ruby
372
- metadata = JSON.parse('{'\
373
- '"email": "testrubyapi@user.com",'\
374
- '"name": "ruby api user",'\
375
- '"custom": "testdata"'\
376
- '}')
377
-
378
- user_models = []
379
-
380
- user_model_A = { "user_id" => "12345",
381
- "company_id" => "67890",
382
- "modified_time" => Time.now.utc.iso8601,
383
- "metadata" => metadata }
384
-
385
- user_model_B = { "user_id" => "1234",
386
- "company_id" => "6789",
387
- "modified_time" => Time.now.utc.iso8601,
388
- "metadata" => metadata }
389
-
390
- user_models << user_model_A << user_model_B
391
- response = MoesifRack::MoesifMiddleware.new(@app, @options).update_users_batch(user_models)
376
+ users = []
377
+
378
+ metadata = {
379
+ :email => 'john@acmeinc.com',
380
+ :first_name => 'John',
381
+ :last_name => 'Doe',
382
+ :title => 'Software Engineer',
383
+ :salesInfo => {
384
+ :stage => 'Customer',
385
+ :lifetime_value => 24000,
386
+ :accountOwner => 'mary@contoso.com',
387
+ }
388
+ }
389
+
390
+ # Campaign object is optional, but useful if you want to track ROI of acquisition channels
391
+ # See https://www.moesif.com/docs/api#users for campaign schema
392
+ campaign = MoesifApi::CampaignModel.new()
393
+ campaign.utm_source = "google"
394
+ campaign.utm_medium = "cpc"
395
+ campaign.utm_campaign = "adwords"
396
+ campaign.utm_term = "api+tooling"
397
+ campaign.utm_content = "landing"
398
+
399
+ # Only user_id is required.
400
+ # metadata can be any custom object
401
+ user = MoesifApi::UserModel.new()
402
+ user.user_id = "12345"
403
+ user.company_id = "67890" # If set, associate user with a company object
404
+ user.campaign = campaign
405
+ user.metadata = metadata
406
+
407
+ users << user
408
+
409
+ response = MoesifRack::MoesifMiddleware.new(@app, @options).update_users_batch(users)
392
410
  ```
393
411
 
394
412
  ## Update Company
395
413
 
396
- ### update_company method
397
- A method is attached to the moesif middleware object to update the company profile or metadata.
398
- The metadata field can be any custom data you want to set on the company. The `company_id` field is required.
414
+ ### Update a Single Company
415
+ Create or update a company profile in Moesif.
416
+ The metadata field can be any company demographic or other info you want to store.
417
+ Only the `company_id` field is required.
418
+ This method is a convenient helper that calls the Moesif API lib.
419
+ For details, visit the [Ruby API Reference](https://www.moesif.com/docs/api?ruby#update-a-company).
399
420
 
400
421
  ```ruby
401
- metadata = JSON.parse('{'\
402
- '"email": "testrubyapi@company.com",'\
403
- '"name": "ruby api company",'\
404
- '"custom": "testdata"'\
405
- '}')
406
-
407
- campaign_model = {"utm_source" => "Adwords",
408
- "utm_medium" => "Twitter"}
422
+ metadata = {
423
+ :org_name => 'Acme, Inc',
424
+ :plan_name => 'Free',
425
+ :deal_stage => 'Lead',
426
+ :mrr => 24000,
427
+ :demographics => {
428
+ :alexa_ranking => 500000,
429
+ :employee_count => 47
430
+ }
431
+ }
409
432
 
410
- company_model = { "company_id" => "12345",
411
- "company_domain" => "acmeinc.com",
412
- "metadata" => metadata,
413
- "campaign" => campaign_model }
433
+ # Campaign object is optional, but useful if you want to track ROI of acquisition channels
434
+ # See https://www.moesif.com/docs/api#update-a-company for campaign schema
435
+ campaign = MoesifApi::CampaignModel.new()
436
+ campaign.utm_source = "google"
437
+ campaign.utm_medium = "cpc"
438
+ campaign.utm_campaign = "adwords"
439
+ campaign.utm_term = "api+tooling"
440
+ campaign.utm_content = "landing"
441
+
442
+ # Only company_id is required.
443
+ # metadata can be any custom object
444
+ company = MoesifApi::CompanyModel.new()
445
+ company.company_id = "67890"
446
+ company.company_domain = "acmeinc.com" # If domain is set, Moesif will enrich your profiles with publicly available info
447
+ company.campaign = campaign
448
+ company.metadata = metadata
414
449
 
415
450
  update_company = MoesifRack::MoesifMiddleware.new(@app, @options).update_company(company_model)
416
451
  ```
417
452
 
418
- ### update_companies_batch method
419
- A method is attached to the moesif middleware object to update the companies profile or metadata in batch.
420
- The metadata field can be any custom data you want to set on the company. The `company_id` field is required.
453
+ ### Update Companies in Batch
454
+ Similar to update_company, but used to update a list of companies in one batch.
455
+ Only the `company_id` field is required.
456
+ This method is a convenient helper that calls the Moesif API lib.
457
+ For details, visit the [Ruby API Reference](https://www.moesif.com/docs/api?ruby#update-companies-in-batch).
421
458
 
422
459
  ```ruby
423
- metadata = JSON.parse('{'\
424
- '"email": "testrubyapi@user.com",'\
425
- '"name": "ruby api user",'\
426
- '"custom": "testdata"'\
427
- '}')
428
-
429
- company_models = []
430
-
431
- company_model_A = { "company_id" => "12345",
432
- "company_domain" => "nowhere.com",
433
- "metadata" => metadata }
434
-
435
- company_model_B = { "company_id" => "67890",
436
- "company_domain" => "acmeinc.com",
437
- "metadata" => metadata }
460
+ companies = []
461
+
462
+ metadata = {
463
+ :org_name => 'Acme, Inc',
464
+ :plan_name => 'Free',
465
+ :deal_stage => 'Lead',
466
+ :mrr => 24000,
467
+ :demographics => {
468
+ :alexa_ranking => 500000,
469
+ :employee_count => 47
470
+ }
471
+ }
438
472
 
439
- company_models << company_model_A << company_model_B
440
- response = MoesifRack::MoesifMiddleware.new(@app, @options).update_companies_batch(company_models)
473
+ # Campaign object is optional, but useful if you want to track ROI of acquisition channels
474
+ # See https://www.moesif.com/docs/api#update-a-company for campaign schema
475
+ campaign = MoesifApi::CampaignModel.new()
476
+ campaign.utm_source = "google"
477
+ campaign.utm_medium = "cpc"
478
+ campaign.utm_campaign = "adwords"
479
+ campaign.utm_term = "api+tooling"
480
+ campaign.utm_content = "landing"
481
+
482
+ # Only company_id is required.
483
+ # metadata can be any custom object
484
+ company = MoesifApi::CompanyModel.new()
485
+ company.company_id = "67890"
486
+ company.company_domain = "acmeinc.com" # If domain is set, Moesif will enrich your profiles with publicly available info
487
+ company.campaign = campaign
488
+ company.metadata = metadata
489
+
490
+ companies << company
491
+ response = MoesifRack::MoesifMiddleware.new(@app, @options).update_companies_batch(companies)
441
492
  ```
442
493
 
443
494
  ## How to test
@@ -19,7 +19,12 @@ class AppConfig
19
19
  puts 'Error getting application configuration, with status code:'
20
20
  puts e.response_code
21
21
  end
22
+ rescue => e
23
+ if debug
24
+ puts e.to_s
25
+ end
22
26
  end
27
+ rescue
23
28
  end
24
29
 
25
30
  def parse_configuration(config_api_response, debug)
@@ -118,4 +123,8 @@ class AppConfig
118
123
  return nil
119
124
  end
120
125
  end
126
+
127
+ def calculate_weight(sample_rate)
128
+ return sample_rate == 0 ? 1 : (100 / sample_rate).floor
129
+ end
121
130
  end
@@ -1,8 +1,8 @@
1
1
 
2
2
  def is_ip?(value)
3
+ ipv4 = /^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/
4
+ ipv6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
3
5
  if
4
- ipv4 = /^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/
5
- ipv6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
6
6
  # We use !! to convert the return value to a boolean
7
7
  !!(value =~ ipv4 or value=~ ipv6)
8
8
  end
@@ -6,6 +6,8 @@ require_relative './client_ip.rb'
6
6
  require_relative './app_config.rb'
7
7
  require_relative './update_user.rb'
8
8
  require_relative './update_company.rb'
9
+ require 'zlib'
10
+ require 'stringio'
9
11
 
10
12
  module MoesifRack
11
13
 
@@ -30,25 +32,29 @@ module MoesifRack
30
32
  @config = @app_config.get_config(@api_controller, @debug)
31
33
  @config_etag = nil
32
34
  @sampling_percentage = 100
33
- @last_updated_time = Time.now.utc
35
+ @last_config_download_time = Time.now.utc
36
+ @last_worker_run = Time.now.utc
34
37
  @config_dict = Hash.new
35
38
  @disable_transaction_id = options['disable_transaction_id'] || false
36
39
  @log_body = options.fetch('log_body', true)
40
+ @batch_size = options['batch_size'] || 25
41
+ @batch_max_time = options['batch_max_time'] || 2
42
+ @events_queue = Queue.new
43
+ @event_response_config_etag = nil
44
+ start_worker()
45
+
37
46
  begin
38
47
  if !@config.nil?
39
- @config_etag, @sampling_percentage, @last_updated_time = @app_config.parse_configuration(@config, @debug)
48
+ @config_etag, @sampling_percentage, @last_config_download_time = @app_config.parse_configuration(@config, @debug)
40
49
  end
41
50
  rescue => exception
42
- if @debug
43
- puts 'Error while parsing application configuration on initialization'
44
- puts exception.to_s
45
- end
51
+ log_debug 'Error while parsing application configuration on initialization'
52
+ log_debug exception.to_s
46
53
  end
47
54
  @capture_outoing_requests = options['capture_outoing_requests']
48
- if @capture_outoing_requests
49
- if @debug
50
- puts 'Start Capturing outgoing requests'
51
- end
55
+ @capture_outgoing_requests = options['capture_outgoing_requests']
56
+ if @capture_outoing_requests || @capture_outgoing_requests
57
+ log_debug 'Start Capturing outgoing requests'
52
58
  require_relative '../../moesif_capture_outgoing/httplog.rb'
53
59
  MoesifCaptureOutgoing.start_capture_outgoing(options)
54
60
  end
@@ -70,12 +76,84 @@ module MoesifRack
70
76
  CompanyHelper.new.update_companies_batch(@api_controller, @debug, company_profiles)
71
77
  end
72
78
 
73
- def call env
74
- start_time = Time.now.utc.iso8601
79
+ def start_with_json(body)
80
+ body.start_with?('{') || body.start_with?('[')
81
+ end
82
+
83
+ def decompress_body(body)
84
+ Zlib::GzipReader.new(StringIO.new(body)).read
85
+ end
86
+
87
+ def transform_headers(headers)
88
+ Hash[headers.map { |k, v| [k.downcase, v]}]
89
+ end
90
+
91
+ def base64_encode_body(body)
92
+ return Base64.encode64(body), 'base64'
93
+ end
75
94
 
95
+ def log_debug(message)
76
96
  if @debug
77
- puts 'inside moesif middleware'
97
+ puts("#{Time.now.to_s} [Moesif Middleware] PID #{Process.pid} TID #{Thread.current.object_id} #{message}")
78
98
  end
99
+ end
100
+
101
+ def parse_body(body, headers)
102
+ begin
103
+ if start_with_json(body)
104
+ parsed_body = JSON.parse(body)
105
+ transfer_encoding = 'json'
106
+ elsif headers.key?('content-encoding') && ((headers['content-encoding'].downcase).include? "gzip")
107
+ uncompressed_string = decompress_body(body)
108
+ parsed_body, transfer_encoding = base64_encode_body(uncompressed_string)
109
+ else
110
+ parsed_body, transfer_encoding = base64_encode_body(body)
111
+ end
112
+ rescue
113
+ parsed_body, transfer_encoding = base64_encode_body(body)
114
+ end
115
+ return parsed_body, transfer_encoding
116
+ end
117
+
118
+ def start_worker
119
+ Thread::new do
120
+ @last_worker_run = Time.now.utc
121
+ loop do
122
+ begin
123
+ until @events_queue.empty? do
124
+ batch_events = []
125
+ until batch_events.size == @batch_size || @events_queue.empty? do
126
+ batch_events << @events_queue.pop
127
+ end
128
+ log_debug("Sending #{batch_events.size.to_s} events to Moesif")
129
+ event_api_response = @api_controller.create_events_batch(batch_events)
130
+ @event_response_config_etag = event_api_response[:x_moesif_config_etag]
131
+ log_debug(event_api_response.to_s)
132
+ log_debug("Events successfully sent to Moesif")
133
+ end
134
+
135
+ if @events_queue.empty?
136
+ log_debug("No events to read from the queue")
137
+ end
138
+
139
+ sleep @batch_max_time
140
+ rescue MoesifApi::APIException => e
141
+ if e.response_code.between?(401, 403)
142
+ puts "Unathorized accesss sending event to Moesif. Please verify your Application Id."
143
+ log_debug(e.to_s)
144
+ end
145
+ log_debug("Error sending event to Moesif, with status code #{e.response_code.to_s}")
146
+ rescue => e
147
+ log_debug(e.to_s)
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ def call env
154
+ start_time = Time.now.utc.iso8601
155
+
156
+ log_debug('Calling Moesif middleware')
79
157
 
80
158
  status, headers, body = @app.call env
81
159
  end_time = Time.now.utc.iso8601
@@ -85,7 +163,7 @@ module MoesifRack
85
163
  complex_copy = env.dup
86
164
 
87
165
  req_headers = {}
88
- complex_copy.select {|k,v| k.start_with? 'HTTP_'}.each do |key, val|
166
+ complex_copy.select {|k,v| k.start_with?('HTTP_', 'CONTENT_') }.each do |key, val|
89
167
  new_key = key.sub(/^HTTP_/, '')
90
168
  new_key = new_key.sub('_', '-')
91
169
  req_headers[new_key] = val
@@ -98,12 +176,7 @@ module MoesifRack
98
176
 
99
177
  if @log_body
100
178
  if req_body_string && req_body_string.length != 0
101
- begin
102
- req_body = JSON.parse(req_body_string)
103
- rescue
104
- req_body = Base64.encode64(req_body_string)
105
- req_body_transfer_encoding = 'base64'
106
- end
179
+ req_body, req_body_transfer_encoding = parse_body(req_body_string, transform_headers(req_headers))
107
180
  end
108
181
  end
109
182
 
@@ -115,12 +188,7 @@ module MoesifRack
115
188
 
116
189
  if @log_body
117
190
  if rsp_body_string && rsp_body_string.length != 0
118
- begin
119
- rsp_body = JSON.parse(rsp_body_string)
120
- rescue
121
- rsp_body = Base64.encode64(rsp_body_string)
122
- rsp_body_transfer_encoding = 'base64'
123
- end
191
+ rsp_body, rsp_body_transfer_encoding = parse_body(rsp_body_string, transform_headers(rsp_headers))
124
192
  end
125
193
  end
126
194
 
@@ -180,43 +248,31 @@ module MoesifRack
180
248
  event_model.direction = "Incoming"
181
249
 
182
250
  if @identify_user
183
- if @debug
184
- puts "calling identify user proc"
185
- end
251
+ log_debug "calling identify user proc"
186
252
  event_model.user_id = @identify_user.call(env, headers, body)
187
253
  end
188
254
 
189
255
  if @identify_company
190
- if @debug
191
- puts "calling identify company proc"
192
- end
256
+ log_debug "calling identify company proc"
193
257
  event_model.company_id = @identify_company.call(env, headers, body)
194
258
  end
195
259
 
196
260
  if @get_metadata
197
- if @debug
198
- puts "calling get_metadata proc"
199
- end
261
+ log_debug "calling get_metadata proc"
200
262
  event_model.metadata = @get_metadata.call(env, headers, body)
201
263
  end
202
264
 
203
265
  if @identify_session
204
- if @debug
205
- puts "calling identify session proc"
206
- end
266
+ log_debug "calling identify session proc"
207
267
  event_model.session_token = @identify_session.call(env, headers, body)
208
268
  end
209
269
  if @mask_data
210
- if @debug
211
- puts "calling mask_data proc"
212
- end
270
+ log_debug "calling mask_data proc"
213
271
  event_model = @mask_data.call(event_model)
214
272
  end
215
273
 
216
- if @debug
217
- puts "sending data to moesif"
218
- puts event_model.to_json
219
- end
274
+ log_debug "sending data to moesif"
275
+ log_debug event_model.to_json
220
276
  # Perform the API call through the SDK function
221
277
  begin
222
278
  @random_percentage = Random.rand(0.00..100.00)
@@ -224,44 +280,35 @@ module MoesifRack
224
280
  begin
225
281
  @sampling_percentage = @app_config.get_sampling_percentage(@config, event_model.user_id, event_model.company_id, @debug)
226
282
  rescue => exception
227
- if @debug
228
- puts 'Error while getting sampling percentage, assuming default behavior'
229
- puts exception.to_s
230
- end
283
+ log_debug 'Error while getting sampling percentage, assuming default behavior'
284
+ log_debug exception.to_s
231
285
  @sampling_percentage = 100
232
286
  end
233
287
 
234
288
  if @sampling_percentage > @random_percentage
235
- event_api_response = @api_controller.create_event(event_model)
236
- event_response_config_etag = event_api_response[:x_moesif_config_etag]
289
+ event_model.weight = @app_config.calculate_weight(@sampling_percentage)
290
+ # Add Event to the queue
291
+ @events_queue << event_model
292
+ log_debug("Event added to the queue ")
293
+ if Time.now.utc > (@last_config_download_time + 60)
294
+ start_worker()
295
+ end
237
296
 
238
- if !event_response_config_etag.nil? && !@config_etag.nil? && @config_etag != event_response_config_etag && Time.now.utc > @last_updated_time + 300
297
+ if !@event_response_config_etag.nil? && !@config_etag.nil? && @config_etag != @event_response_config_etag && Time.now.utc > (@last_config_download_time + 300)
239
298
  begin
240
299
  @config = @app_config.get_config(@api_controller, @debug)
241
- @config_etag, @sampling_percentage, @last_updated_time = @app_config.parse_configuration(@config, @debug)
300
+ @config_etag, @sampling_percentage, @last_config_download_time = @app_config.parse_configuration(@config, @debug)
242
301
  rescue => exception
243
- if @debug
244
- puts 'Error while updating the application configuration'
245
- puts exception.to_s
246
- end
302
+ log_debug 'Error while updating the application configuration'
303
+ log_debug exception.to_s
247
304
  end
248
305
  end
249
- if @debug
250
- puts("Event successfully sent to Moesif")
251
- end
252
306
  else
253
- if @debug
254
- puts("Skipped Event due to sampling percentage: " + @sampling_percentage.to_s + " and random percentage: " + @random_percentage.to_s)
255
- end
256
- end
257
- rescue MoesifApi::APIException => e
258
- if e.response_code.between?(401, 403)
259
- puts "Unathorized accesss sending event to Moesif. Please verify your Application Id."
260
- end
261
- if @debug
262
- puts "Error sending event to Moesif, with status code: "
263
- puts e.response_code
307
+ log_debug("Skipped Event due to sampling percentage: " + @sampling_percentage.to_s + " and random percentage: " + @random_percentage.to_s)
264
308
  end
309
+ rescue => exception
310
+ log_debug "Error adding event to the queue "
311
+ log_debug exception.to_s
265
312
  end
266
313
 
267
314
  end
@@ -275,11 +322,7 @@ module MoesifRack
275
322
  end
276
323
 
277
324
  if !should_skip
278
- if @debug
279
- process_send.call
280
- else
281
- Thread.start(&process_send)
282
- end
325
+ process_send.call
283
326
  end
284
327
 
285
328
  [status, headers, body]
@@ -3,6 +3,7 @@ require 'rack'
3
3
  require 'moesif_api'
4
4
  require 'json'
5
5
  require 'base64'
6
+ require_relative '../../lib/moesif_rack/app_config.rb'
6
7
 
7
8
  module MoesifCaptureOutgoing
8
9
 
@@ -23,6 +24,22 @@ module MoesifCaptureOutgoing
23
24
  @skip_outgoing = options['skip_outgoing']
24
25
  @mask_data_outgoing = options['mask_data_outgoing']
25
26
  @log_body_outgoing = options.fetch('log_body_outgoing', true)
27
+ @app_config = AppConfig.new
28
+ @config = @app_config.get_config(@api_controller, @debug)
29
+ @config_etag = nil
30
+ @sampling_percentage = 100
31
+ @last_updated_time = Time.now.utc
32
+ @config_dict = Hash.new
33
+ begin
34
+ if !@config.nil?
35
+ @config_etag, @sampling_percentage, @last_updated_time = @app_config.parse_configuration(@config, @debug)
36
+ end
37
+ rescue => exception
38
+ if @debug
39
+ puts 'Error while parsing application configuration on initialization'
40
+ puts exception.to_s
41
+ end
42
+ end
26
43
  end
27
44
 
28
45
  def call (url, request, request_time, response, response_time)
@@ -157,11 +174,45 @@ module MoesifCaptureOutgoing
157
174
 
158
175
  # Send Event to Moesif
159
176
  begin
160
- if @debug
161
- puts 'Sending Outgoing Request Data to Moesif'
162
- puts event_model.to_json
177
+ @random_percentage = Random.rand(0.00..100.00)
178
+ begin
179
+ @sampling_percentage = @app_config.get_sampling_percentage(@config, event_model.user_id, event_model.company_id, @debug)
180
+ rescue => exception
181
+ if @debug
182
+ puts 'Error while getting sampling percentage, assuming default behavior'
183
+ puts exception.to_s
184
+ end
185
+ @sampling_percentage = 100
186
+ end
187
+
188
+ if @sampling_percentage > @random_percentage
189
+ event_model.weight = @app_config.calculate_weight(@sampling_percentage)
190
+ if @debug
191
+ puts 'Sending Outgoing Request Data to Moesif'
192
+ puts event_model.to_json
193
+ end
194
+ event_api_response = @api_controller.create_event(event_model)
195
+ event_response_config_etag = event_api_response[:x_moesif_config_etag]
196
+
197
+ if !event_response_config_etag.nil? && !@config_etag.nil? && @config_etag != event_response_config_etag && Time.now.utc > @last_updated_time + 300
198
+ begin
199
+ @config = @app_config.get_config(@api_controller, @debug)
200
+ @config_etag, @sampling_percentage, @last_updated_time = @app_config.parse_configuration(@config, @debug)
201
+ rescue => exception
202
+ if @debug
203
+ puts 'Error while updating the application configuration'
204
+ puts exception.to_s
205
+ end
206
+ end
207
+ end
208
+ if @debug
209
+ puts("Event successfully sent to Moesif")
210
+ end
211
+ else
212
+ if @debug
213
+ puts("Skipped outgoing Event due to sampling percentage: " + @sampling_percentage.to_s + " and random percentage: " + @random_percentage.to_s)
214
+ end
163
215
  end
164
- @api_controller.create_event(event_model)
165
216
  rescue MoesifApi::APIException => e
166
217
  if e.response_code.between?(401, 403)
167
218
  puts "Unathorized accesss sending event to Moesif. Please verify your Application Id."
@@ -170,6 +221,10 @@ module MoesifCaptureOutgoing
170
221
  puts "Error sending event to Moesif, with status code: "
171
222
  puts e.response_code
172
223
  end
224
+ rescue => e
225
+ if debug
226
+ puts e.to_s
227
+ end
173
228
  end
174
229
  else
175
230
  if @debug
@@ -10,7 +10,7 @@ class MoesifRackTest < Test::Unit::TestCase
10
10
  @options = { 'application_id' => 'Your Moesif Application Id',
11
11
  'debug' => true,
12
12
  'disable_transaction_id' => true,
13
- 'capture_outoing_requests' => true,
13
+ 'capture_outgoing_requests' => true,
14
14
  'get_metadata' => Proc.new {|request, response|
15
15
  {
16
16
  'foo' => 'abc',
@@ -108,6 +108,8 @@ class MoesifRackTest < Test::Unit::TestCase
108
108
 
109
109
  def test_log_event
110
110
  response = @moesif_rack_app.call(Rack::MockRequest.env_for("https://acmeinc.com/items/42752/reviews"))
111
+ # Sleep to allow queue to flush for testing purpose
112
+ sleep 5
111
113
  assert_equal response, @app.call(nil)
112
114
  end
113
115
 
metadata CHANGED
@@ -1,28 +1,34 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moesif_rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.9
4
+ version: 1.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Moesif, Inc
8
8
  - Xing Wang
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-12-13 00:00:00.000000000 Z
12
+ date: 2020-06-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: test-unit
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '3.1'
21
+ - - ">="
19
22
  - !ruby/object:Gem::Version
20
23
  version: 3.1.5
21
- type: :runtime
24
+ type: :development
22
25
  prerelease: false
23
26
  version_requirements: !ruby/object:Gem::Requirement
24
27
  requirements:
25
28
  - - "~>"
29
+ - !ruby/object:Gem::Version
30
+ version: '3.1'
31
+ - - ">="
26
32
  - !ruby/object:Gem::Version
27
33
  version: 3.1.5
28
34
  - !ruby/object:Gem::Dependency
@@ -31,15 +37,21 @@ dependencies:
31
37
  requirements:
32
38
  - - "~>"
33
39
  - !ruby/object:Gem::Version
34
- version: 1.2.10
40
+ version: '1.2'
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.2.12
35
44
  type: :runtime
36
45
  prerelease: false
37
46
  version_requirements: !ruby/object:Gem::Requirement
38
47
  requirements:
39
48
  - - "~>"
40
49
  - !ruby/object:Gem::Version
41
- version: 1.2.10
42
- description: Collection/Data Ingestion SDK for Rack (also Rails) Middleware / RoR
50
+ version: '1.2'
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.2.12
54
+ description: Rack/Rails middleware to log API calls to Moesif API analytics and monitoring
43
55
  email: xing@moesif.com
44
56
  executables: []
45
57
  extensions: []
@@ -61,7 +73,7 @@ homepage: https://moesif.com
61
73
  licenses:
62
74
  - Apache-2.0
63
75
  metadata: {}
64
- post_install_message:
76
+ post_install_message:
65
77
  rdoc_options: []
66
78
  require_paths:
67
79
  - lib
@@ -76,9 +88,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
88
  - !ruby/object:Gem::Version
77
89
  version: '0'
78
90
  requirements: []
79
- rubyforge_project:
91
+ rubyforge_project:
80
92
  rubygems_version: 2.7.7
81
- signing_key:
93
+ signing_key:
82
94
  specification_version: 4
83
95
  summary: moesif_rack
84
96
  test_files: []