firehose_integration 0.0.1

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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +34 -0
  5. data/lib/firehose_integration.rb +9 -0
  6. data/lib/firehose_integration/active_record_relation.rb +6 -0
  7. data/lib/firehose_integration/jobs/kinesis_bulk_job.rb +22 -0
  8. data/lib/firehose_integration/jobs/kinesis_job.rb +16 -0
  9. data/lib/firehose_integration/jobs/kinesis_single_object_job.rb +24 -0
  10. data/lib/firehose_integration/models/concerns/kinesis_event.rb +71 -0
  11. data/lib/firehose_integration/version.rb +3 -0
  12. data/lib/tasks/firehose_integration_tasks.rake +4 -0
  13. data/test/cassettes/kinesis_bulk_failure.yml +55 -0
  14. data/test/cassettes/kinesis_bulk_success.yml +52 -0
  15. data/test/cassettes/kinesis_failure.yml +55 -0
  16. data/test/cassettes/kinesis_success.yml +52 -0
  17. data/test/dummy/README.rdoc +28 -0
  18. data/test/dummy/Rakefile +6 -0
  19. data/test/dummy/app/assets/javascripts/application.js +13 -0
  20. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  21. data/test/dummy/app/controllers/application_controller.rb +5 -0
  22. data/test/dummy/app/helpers/application_helper.rb +2 -0
  23. data/test/dummy/app/kinesis_serializers/dummy_model_kinesis_serializer.rb +17 -0
  24. data/test/dummy/app/models/dummy_model.rb +3 -0
  25. data/test/dummy/app/models/stupid_model.rb +3 -0
  26. data/test/dummy/app/views/layouts/application.html.erb +13 -0
  27. data/test/dummy/bin/bundle +3 -0
  28. data/test/dummy/bin/rails +4 -0
  29. data/test/dummy/bin/rake +4 -0
  30. data/test/dummy/bin/setup +29 -0
  31. data/test/dummy/config.ru +4 -0
  32. data/test/dummy/config/application.rb +32 -0
  33. data/test/dummy/config/boot.rb +5 -0
  34. data/test/dummy/config/database.yml +25 -0
  35. data/test/dummy/config/environment.rb +5 -0
  36. data/test/dummy/config/environments/development.rb +41 -0
  37. data/test/dummy/config/environments/production.rb +79 -0
  38. data/test/dummy/config/environments/test.rb +42 -0
  39. data/test/dummy/config/initializers/assets.rb +11 -0
  40. data/test/dummy/config/initializers/aws.rb +6 -0
  41. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  42. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  43. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  44. data/test/dummy/config/initializers/inflections.rb +16 -0
  45. data/test/dummy/config/initializers/mime_types.rb +4 -0
  46. data/test/dummy/config/initializers/session_store.rb +3 -0
  47. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  48. data/test/dummy/config/locales/en.yml +23 -0
  49. data/test/dummy/config/routes.rb +56 -0
  50. data/test/dummy/config/secrets.yml +22 -0
  51. data/test/dummy/db/development.sqlite3 +0 -0
  52. data/test/dummy/db/migrate/20160526190814_create_dummy_models.rb +7 -0
  53. data/test/dummy/db/migrate/20160526203033_create_stupid_model.rb +7 -0
  54. data/test/dummy/db/schema.rb +26 -0
  55. data/test/dummy/db/test.sqlite3 +0 -0
  56. data/test/dummy/log/development.log +34 -0
  57. data/test/dummy/log/test.log +3669 -0
  58. data/test/dummy/public/404.html +67 -0
  59. data/test/dummy/public/422.html +67 -0
  60. data/test/dummy/public/500.html +66 -0
  61. data/test/dummy/public/favicon.ico +0 -0
  62. data/test/firehose_integration_test.rb +7 -0
  63. data/test/fixtures/dummy_models.yml +3 -0
  64. data/test/jobs/kinesis_bulk_job_test.rb +26 -0
  65. data/test/jobs/kinesis_job_test.rb +26 -0
  66. data/test/jobs/kinesis_single_object_test.rb +39 -0
  67. data/test/models/concerns/kinesis_event_test.rb +27 -0
  68. data/test/test_helper.rb +29 -0
  69. metadata +266 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ba2eded2ae69513146014504efed4d2aaa8bc666
4
+ data.tar.gz: 91994144647e8daeaab259af8f9e0dcfa7242b5a
5
+ SHA512:
6
+ metadata.gz: b5604bb6df7f159e31c5837603cce360fbf390a0d66f59e0a6eb62874d0c2643637bb2d047774e8eafd97851411b5205cc707d95efab9d5ad7975f6fbc481197
7
+ data.tar.gz: 0a75b00665f45736d94974a8ca275b4960eb8b3f554b3fa0c69472a5e3cfdfed02eef6760e18ae0ebf56f84af0af7f24c526467fcf61d9d65619bddfa5a18be2
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 onomojo
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = FirehoseIntegration
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'FirehoseIntegration'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,9 @@
1
+ require "firehose_integration/active_record_relation"
2
+
3
+ require "firehose_integration/jobs/kinesis_job"
4
+ require "firehose_integration/jobs/kinesis_bulk_job"
5
+ require "firehose_integration/jobs/kinesis_single_object_job"
6
+
7
+ require "firehose_integration/models/concerns/kinesis_event"
8
+ module FirehoseIntegration
9
+ end
@@ -0,0 +1,6 @@
1
+ class ActiveRecord::Relation
2
+ def update_all_with_kinesis(params)
3
+ self.update_all params
4
+ FirehoseIntegration::KinesisSingleObjectJob.perform_later(ancestors.first.to_s, self.pluck(:id))
5
+ end
6
+ end
@@ -0,0 +1,22 @@
1
+ module FirehoseIntegration
2
+ class KinesisBulkJob < ActiveJob::Base
3
+ queue_as :kinesis_events_bulk
4
+ def perform(stream, data)
5
+ client = Aws::Firehose::Client.new(region:'us-east-1')
6
+
7
+ records = []
8
+ data.each do |d|
9
+ records << {
10
+ data: "#{d}\n"
11
+ }
12
+ end
13
+
14
+ params = {
15
+ delivery_stream_name: stream,
16
+ records: records
17
+ }
18
+
19
+ client.put_record_batch params
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ module FirehoseIntegration
2
+ class KinesisJob < ActiveJob::Base
3
+ queue_as :kinesis_events
4
+ def perform(stream, data)
5
+ client = Aws::Firehose::Client.new(region:'us-east-1')
6
+
7
+ params = {
8
+ delivery_stream_name: stream,
9
+ record: {
10
+ data: "#{data}\n"
11
+ }
12
+ }
13
+ client.put_record params
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ module FirehoseIntegration
2
+ class KinesisSingleObjectJob < ActiveJob::Base
3
+ queue_as :kinesis_events_single
4
+
5
+ def perform(class_name, ids)
6
+ client = Aws::Firehose::Client.new(region:'us-east-1')
7
+ results = []
8
+ ids.each do |id|
9
+ object = class_name.constantize.find(id)
10
+ stream = object.class.kinesis_stream_name
11
+ data = object.to_kinesis
12
+
13
+ params = {
14
+ delivery_stream_name: stream,
15
+ record: {
16
+ data: "#{data}\n"
17
+ }
18
+ }
19
+ results << client.put_record(params)
20
+ end
21
+ results
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,71 @@
1
+ module FirehoseIntegration
2
+ module KinesisEvent
3
+ extend ActiveSupport::Concern
4
+
5
+ MAX_REDSHIFT_STRING_SIZE = 65535
6
+
7
+ module ClassMethods
8
+ def firehose_integratable
9
+ after_commit :send_kinesis_event, unless: Proc.new { |instance| instance.try(:skip_kinesis_event) }
10
+
11
+ begin
12
+ include "#{self.model_name.name}KinesisSerializer".constantize
13
+ rescue
14
+ end
15
+ end
16
+
17
+ def kinesis_stream_name
18
+ raise(NoMethodError, "Model must define class method kinesis_stream_name")
19
+ end
20
+ end
21
+
22
+ included do
23
+
24
+ def to_kinesis
25
+ raise(NoMethodError, "Model must define instance method to_kinesis")
26
+ end
27
+
28
+ def prepare_for_redshift(field)
29
+ if field.present?
30
+ if field.is_a? Array
31
+ output = []
32
+ field.each do |f|
33
+ output << massage_data_for_redshift(f)
34
+ end
35
+ return output.join("|")
36
+ end
37
+ end
38
+ end
39
+
40
+ def massage_data_for_redshift field
41
+ if field.is_a? String
42
+ output = escape_string_for_redshift(field).truncate(MAX_REDSHIFT_STRING_SIZE)
43
+ Rails.logger.info "Redshift data has been truncated due to length: #{output.truncate(50)}" if output.size == MAX_REDSHIFT_STRING_SIZE
44
+ output
45
+ else
46
+ field
47
+ end
48
+ end
49
+
50
+ def escape_string_for_redshift field
51
+ return field unless field.is_a? String
52
+ output = field
53
+ {
54
+ '\n' => '\\n',
55
+ '\r' => '\\r',
56
+ '\|' => '&verbar;'
57
+ }.each do |pattern, replacement|
58
+ output = output.gsub(Regexp.new(pattern), replacement)
59
+ end
60
+ output
61
+ end
62
+
63
+
64
+ def send_kinesis_event
65
+ KinesisJob.perform_later(self.class.kinesis_stream_name, self.to_kinesis)
66
+ self.kinesis_extra_serialization if self.methods.include? :kinesis_extra_serialization
67
+ end
68
+ end
69
+ end
70
+ end
71
+ ActiveRecord::Base.send :include, FirehoseIntegration::KinesisEvent
@@ -0,0 +1,3 @@
1
+ module FirehoseIntegration
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :firehose_integration do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,55 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://firehose.us-east-1.amazonaws.com/
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"DeliveryStreamName":"invalid-stream-name","Records":[{"Data":"c29tZSBkYXRhCg=="}]}'
9
+ headers:
10
+ Content-Type:
11
+ - application/x-amz-json-1.1
12
+ Accept-Encoding:
13
+ - ''
14
+ User-Agent:
15
+ - aws-sdk-ruby2/2.3.8 ruby/2.2.1 x86_64-darwin15
16
+ X-Amz-Target:
17
+ - Firehose_20150804.PutRecordBatch
18
+ X-Amz-Date:
19
+ - 20160526T204248Z
20
+ Host:
21
+ - firehose.us-east-1.amazonaws.com
22
+ X-Amz-Content-Sha256:
23
+ - 51d39395cb7e526eb7bac24de83e3012137401a5d77dcaf4308119c1c69a8f33
24
+ Authorization:
25
+ - AWS4-HMAC-SHA256 Credential=akid/20160526/us-east-1/firehose/aws4_request,
26
+ SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target,
27
+ Signature=bb4def5cb89830d9f160f15e40b4327f556bd719d7b9cc1c06084bf43e342ef2
28
+ Content-Length:
29
+ - '84'
30
+ Accept:
31
+ - "*/*"
32
+ response:
33
+ status:
34
+ code: 400
35
+ message: Bad Request
36
+ headers:
37
+ X-Amzn-Requestid:
38
+ - 69cc5bea-2382-11e6-aaa0-171acafcc56d
39
+ X-Amz-Id-2:
40
+ - sJghDMvGF8Lt4ULOjnCoI0pj42bU/9gRZ+uLaCTZCDaRcKRyVHnr9GcucTdP8S8fgkWwPsVY3+o8tUzVXoMzHg==
41
+ Content-Type:
42
+ - application/x-amz-json-1.1
43
+ Content-Length:
44
+ - '117'
45
+ Date:
46
+ - Thu, 26 May 2016 20:42:50 GMT
47
+ Connection:
48
+ - close
49
+ body:
50
+ encoding: UTF-8
51
+ string: '{"__type":"ResourceNotFoundException","message":"Firehose invalid-stream-name
52
+ not found under account 000111222333."}'
53
+ http_version:
54
+ recorded_at: Thu, 26 May 2016 20:42:48 GMT
55
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,52 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://firehose.us-east-1.amazonaws.com/
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"DeliveryStreamName":"test-stream","Records":[{"Data":"c29tZSBkYXRhCg=="},{"Data":"bW9yZSBkYXRhCg=="}]}'
9
+ headers:
10
+ Content-Type:
11
+ - application/x-amz-json-1.1
12
+ Accept-Encoding:
13
+ - ''
14
+ User-Agent:
15
+ - aws-sdk-ruby2/2.3.8 ruby/2.2.1 x86_64-darwin15
16
+ X-Amz-Target:
17
+ - Firehose_20150804.PutRecordBatch
18
+ X-Amz-Date:
19
+ - 20160526T204248Z
20
+ Host:
21
+ - firehose.us-east-1.amazonaws.com
22
+ X-Amz-Content-Sha256:
23
+ - 46abc6a5b3638e3d8b76072597aade6f9ebad6eee1e4935c232a1be5e3842214
24
+ Authorization:
25
+ - AWS4-HMAC-SHA256 Credential=akid/20160526/us-east-1/firehose/aws4_request,
26
+ SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target,
27
+ Signature=bad19647525c88c8307912d614088b00d9c26a644934eac53c62530eacef1ef6
28
+ Content-Length:
29
+ - '104'
30
+ Accept:
31
+ - "*/*"
32
+ response:
33
+ status:
34
+ code: 200
35
+ message: OK
36
+ headers:
37
+ X-Amzn-Requestid:
38
+ - 6a098c7c-2382-11e6-9ea8-514fc73df602
39
+ X-Amz-Id-2:
40
+ - 8ZperIhPiVSMSHSeCAxfkbqbqgyofdOwaTrcrhvi8UP/ocNmmlKYbSvsAoFKhjVO0qH0XHXF3SoaETk+QvZhjw==
41
+ Content-Type:
42
+ - application/x-amz-json-1.1
43
+ Content-Length:
44
+ - '90'
45
+ Date:
46
+ - Thu, 17 Mar 2016 15:43:58 GMT
47
+ body:
48
+ encoding: UTF-8
49
+ string: '{"FailedPutCount":0,"RequestResponses":[{"RecordId":"SOMEID"},{"RecordId":"SOMEOTHERID"}]}'
50
+ http_version:
51
+ recorded_at: Thu, 17 Mar 2016 15:43:58 GMT
52
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,55 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://firehose.us-east-1.amazonaws.com/
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"DeliveryStreamName":"invalid-stream-name","Record":{"Data":"c29tZSBkYXRhCg=="}}'
9
+ headers:
10
+ Content-Type:
11
+ - application/x-amz-json-1.1
12
+ Accept-Encoding:
13
+ - ''
14
+ User-Agent:
15
+ - aws-sdk-ruby2/2.3.8 ruby/2.2.1 x86_64-darwin15
16
+ X-Amz-Target:
17
+ - Firehose_20150804.PutRecord
18
+ X-Amz-Date:
19
+ - 20160526T210311Z
20
+ Host:
21
+ - firehose.us-east-1.amazonaws.com
22
+ X-Amz-Content-Sha256:
23
+ - 174411b44d27135ef894b2bfc487dacf8753738bc6070405ece770a2c5acfd65
24
+ Authorization:
25
+ - AWS4-HMAC-SHA256 Credential=akid/20160526/us-east-1/firehose/aws4_request,
26
+ SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target,
27
+ Signature=ae6474afcfb57bd28f8540c6e43fb12bae6cd41fe2db9b1994ce47c514b87703
28
+ Content-Length:
29
+ - '81'
30
+ Accept:
31
+ - "*/*"
32
+ response:
33
+ status:
34
+ code: 400
35
+ message: Bad Request
36
+ headers:
37
+ X-Amzn-Requestid:
38
+ - 42fabd77-2385-11e6-be61-91f005091ebf
39
+ X-Amz-Id-2:
40
+ - XKgPSA6n5lN6sk6tvNz+5wUS46riOeVei6jvqCPucXpQEy2/EzjzpzwHn8hknKQg8dWvq9iVcAjfH66qN0C4JdkRjEdnQQFn
41
+ Content-Type:
42
+ - application/x-amz-json-1.1
43
+ Content-Length:
44
+ - '117'
45
+ Date:
46
+ - Thu, 26 May 2016 21:03:14 GMT
47
+ Connection:
48
+ - close
49
+ body:
50
+ encoding: UTF-8
51
+ string: '{"__type":"ResourceNotFoundException","message":"Firehose invalid-stream-name
52
+ not found under account 000111222333."}'
53
+ http_version:
54
+ recorded_at: Thu, 26 May 2016 21:03:12 GMT
55
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,52 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://firehose.us-east-1.amazonaws.com/
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"DeliveryStreamName":"some-stream","Record":{"Data":"c29tZSBkYXRhCg=="}}'
9
+ headers:
10
+ Content-Type:
11
+ - application/x-amz-json-1.1
12
+ Accept-Encoding:
13
+ - ''
14
+ User-Agent:
15
+ - aws-sdk-ruby2/2.3.8 ruby/2.2.1 x86_64-darwin15
16
+ X-Amz-Target:
17
+ - Firehose_20150804.PutRecord
18
+ X-Amz-Date:
19
+ - 20160526T205525Z
20
+ Host:
21
+ - firehose.us-east-1.amazonaws.com
22
+ X-Amz-Content-Sha256:
23
+ - b770027680e36df60472bda3598bc24ab1b28083f6c0bae0f858715d2830ed83
24
+ Authorization:
25
+ - AWS4-HMAC-SHA256 Credential=akid/20160526/us-east-1/firehose/aws4_request,
26
+ SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target,
27
+ Signature=5dd75f094a5b67f726c7ea3333102a3d9a870975e9357efa48d13c35ae5e26c6
28
+ Content-Length:
29
+ - '73'
30
+ Accept:
31
+ - "*/*"
32
+ response:
33
+ status:
34
+ code: 200
35
+ message: OK
36
+ headers:
37
+ X-Amzn-Requestid:
38
+ - 2cfe307f-2384-11e6-9868-69152542a0c3
39
+ X-Amz-Id-2:
40
+ - GWN297SaqQ9EdAgTzh9MzxSU3OFyemuVtiiKIz+idFwspK9imwSwhLzEM3jjVqTl8joR1DcvfFO4riIj0spnOfjRoBqHS5cC
41
+ Content-Type:
42
+ - application/x-amz-json-1.1
43
+ Content-Length:
44
+ - '21'
45
+ Date:
46
+ - Thu, 26 May 2016 20:55:27 GMT
47
+ body:
48
+ encoding: UTF-8
49
+ string: '{"RecordId":"SOMEID"}'
50
+ http_version:
51
+ recorded_at: Thu, 26 May 2016 20:55:25 GMT
52
+ recorded_with: VCR 2.9.3