tartarus-rb 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1336e4444881135833d1d1784514a523a9abce37fe911683398b3f2b2bac0c6
4
- data.tar.gz: 82ec19e8f7ea7bde9df2d8d9c75c218f152832bfb977963489e07e3ffbff6cb3
3
+ metadata.gz: ee9118a9365d2a74dd802218b0b0b36286c6494ecffe4f81cbf9bfed35597057
4
+ data.tar.gz: f85ffe0e1f182667b5b9b522746a85b45cc9cd3452270f583549a0616d06337c
5
5
  SHA512:
6
- metadata.gz: 13139b7f8b5d5397cd0bd5d7104ed676c26345197875824eab50b7eebd3a50a4a983e44c73788da33490c2f5ba610422b643104c55e23b004817a016c4d56f44
7
- data.tar.gz: 4622b5a42e74f7802e27f8bcb48959f9d6957a334ec7647f184b85bc09736718fc1e653da2a416d5c8d08417372eebeb89f869b099bb732085fffaa86fbb9506
6
+ metadata.gz: '08e1ab969f1681fca948bdfb4ae98fb919e7caef4e95a1cf5a3a2dffc6fa79a59c73655927be759408f951ad0846ff5c1dea6b7396220801f31c179730e9d6e3'
7
+ data.tar.gz: 72f485499631b46912293650672cd79e8df364ebbc7b18144731c1077e7fa8157be8b20e4078212b04dfb539a3de19af99abcca720163785c3132f51bf574968
data/.env.sample ADDED
@@ -0,0 +1,4 @@
1
+ AWS_REGION=""
2
+ AWS_SECRET=""
3
+ AWS_KEY=""
4
+ VAULT_NAME=""
data/.gitignore CHANGED
@@ -7,6 +7,8 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  /spec/test.db
10
+ /spec/tmp/
11
+ .env
10
12
 
11
13
  # rspec failure tracking
12
14
  .rspec_status
data/.travis.yml CHANGED
@@ -3,6 +3,13 @@ language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
5
  - 2.7.2
6
+ env:
7
+ global:
8
+ - AWS_KEY=AWS_KEY
9
+ - AWS_SECRET=AWS_SECRET
10
+ - AWS_REGION="us-east-2"
11
+ - VAULT_NAME=VAULT_NAME
6
12
  before_install: gem install bundler -v 2.1.4
7
13
  services:
8
14
  - redis-server
15
+ - postgresql
data/Changelog.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## Master
4
+
5
+ ## 0.4.0
6
+
7
+ - Add Glacier remote storage support to upload data before deleting it
8
+
3
9
  ## 0.3.0
4
10
 
5
11
  - Add `delete_all_using_limit_in_batches` strategy
data/Gemfile.lock CHANGED
@@ -1,13 +1,27 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tartarus-rb (0.3.0)
4
+ tartarus-rb (0.4.0)
5
+ aws-sdk-glacier
5
6
  sidekiq (>= 5)
6
7
  sidekiq-cron (~> 1)
7
8
 
8
9
  GEM
9
10
  remote: https://rubygems.org/
10
11
  specs:
12
+ actionpack (6.1.0.rc1)
13
+ actionview (= 6.1.0.rc1)
14
+ activesupport (= 6.1.0.rc1)
15
+ rack (~> 2.0, >= 2.0.9)
16
+ rack-test (>= 0.6.3)
17
+ rails-dom-testing (~> 2.0)
18
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
19
+ actionview (6.1.0.rc1)
20
+ activesupport (= 6.1.0.rc1)
21
+ builder (~> 3.1)
22
+ erubi (~> 1.4)
23
+ rails-dom-testing (~> 2.0)
24
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
11
25
  activemodel (6.1.0.rc1)
12
26
  activesupport (= 6.1.0.rc1)
13
27
  activerecord (6.1.0.rc1)
@@ -19,21 +33,74 @@ GEM
19
33
  minitest (>= 5.1)
20
34
  tzinfo (~> 2.0)
21
35
  zeitwerk (~> 2.3)
36
+ addressable (2.7.0)
37
+ public_suffix (>= 2.0.2, < 5.0)
38
+ aws-eventstream (1.1.0)
39
+ aws-partitions (1.424.0)
40
+ aws-sdk-core (3.112.0)
41
+ aws-eventstream (~> 1, >= 1.0.2)
42
+ aws-partitions (~> 1, >= 1.239.0)
43
+ aws-sigv4 (~> 1.1)
44
+ jmespath (~> 1.0)
45
+ aws-sdk-glacier (1.36.0)
46
+ aws-sdk-core (~> 3, >= 3.112.0)
47
+ aws-sigv4 (~> 1.1)
48
+ aws-sigv4 (1.2.2)
49
+ aws-eventstream (~> 1, >= 1.0.2)
50
+ builder (3.2.4)
22
51
  concurrent-ruby (1.1.7)
23
52
  connection_pool (2.2.3)
53
+ crack (0.4.3)
54
+ safe_yaml (~> 1.0.0)
55
+ crass (1.0.6)
24
56
  diff-lcs (1.4.4)
57
+ dotenv (2.7.6)
58
+ erubi (1.10.0)
25
59
  et-orbi (1.2.4)
26
60
  tzinfo
27
- fugit (1.4.1)
61
+ fugit (1.4.2)
28
62
  et-orbi (~> 1.1, >= 1.1.8)
29
63
  raabro (~> 1.4)
64
+ hashdiff (1.0.0)
30
65
  i18n (1.8.5)
31
66
  concurrent-ruby (~> 1.0)
67
+ jmespath (1.4.0)
68
+ loofah (2.9.0)
69
+ crass (~> 1.0.2)
70
+ nokogiri (>= 1.5.9)
71
+ method_source (1.0.0)
72
+ mini_portile2 (2.5.0)
32
73
  minitest (5.14.2)
74
+ nokogiri (1.11.1)
75
+ mini_portile2 (~> 2.5.0)
76
+ racc (~> 1.4)
77
+ pg (1.2.3)
78
+ postgres-copy (1.5.0)
79
+ activerecord (>= 5.1)
80
+ pg (>= 0.17)
81
+ responders
82
+ public_suffix (4.0.4)
33
83
  raabro (1.4.0)
84
+ racc (1.5.2)
34
85
  rack (2.2.3)
86
+ rack-test (1.1.0)
87
+ rack (>= 1.0, < 3)
88
+ rails-dom-testing (2.0.3)
89
+ activesupport (>= 4.2.0)
90
+ nokogiri (>= 1.6)
91
+ rails-html-sanitizer (1.3.0)
92
+ loofah (~> 2.3)
93
+ railties (6.1.0.rc1)
94
+ actionpack (= 6.1.0.rc1)
95
+ activesupport (= 6.1.0.rc1)
96
+ method_source
97
+ rake (>= 0.8.7)
98
+ thor (~> 1.0)
35
99
  rake (13.0.1)
36
100
  redis (4.2.2)
101
+ responders (3.0.1)
102
+ actionpack (>= 5.0)
103
+ railties (>= 5.0)
37
104
  rspec (3.9.0)
38
105
  rspec-core (~> 3.9.0)
39
106
  rspec-expectations (~> 3.9.0)
@@ -50,6 +117,7 @@ GEM
50
117
  rspec-core (~> 3.0, >= 3.0.0)
51
118
  sidekiq (>= 2.4.0)
52
119
  rspec-support (3.9.3)
120
+ safe_yaml (1.0.5)
53
121
  sidekiq (6.1.2)
54
122
  connection_pool (>= 2.2.2)
55
123
  rack (~> 2.0)
@@ -57,9 +125,15 @@ GEM
57
125
  sidekiq-cron (1.2.0)
58
126
  fugit (~> 1.1)
59
127
  sidekiq (>= 4.2.1)
60
- sqlite3 (1.4.2)
128
+ thor (1.1.0)
129
+ timecop (0.9.2)
61
130
  tzinfo (2.0.2)
62
131
  concurrent-ruby (~> 1.0)
132
+ vcr (6.0.0)
133
+ webmock (3.7.6)
134
+ addressable (>= 2.3.6)
135
+ crack (>= 0.3.2)
136
+ hashdiff (>= 0.4.0, < 2.0.0)
63
137
  zeitwerk (2.4.1)
64
138
 
65
139
  PLATFORMS
@@ -67,11 +141,16 @@ PLATFORMS
67
141
 
68
142
  DEPENDENCIES
69
143
  activerecord (~> 6)
144
+ dotenv
145
+ pg
146
+ postgres-copy
70
147
  rake (~> 13.0)
71
148
  rspec (~> 3.0)
72
149
  rspec-sidekiq
73
- sqlite3
74
150
  tartarus-rb!
151
+ timecop
152
+ vcr
153
+ webmock
75
154
 
76
155
  BUNDLED WITH
77
156
  2.1.4
data/README.md CHANGED
@@ -56,12 +56,22 @@ if File.exist?(schedule_file) && Sidekiq.server?
56
56
  item.timestamp_field = :created_at
57
57
  end
58
58
 
59
+ glacier_configuration = Tartarus::RemoteStorage::Glacier::Configuration.build(
60
+ aws_key: ENV.fetch("AWS_KEY"),
61
+ aws_secret: ENV.fetch("AWS_SECRET"),
62
+ aws_region: ENV.fetch("AWS_REGION"),
63
+ vault_name: ENV.fetch("GLACIER_VAULT_NAME"),
64
+ root_path: Rails.root.to_s,
65
+ archive_registry_factory: ArchiveRegistry,
66
+ )
67
+
59
68
  tartarus.register do |item|
60
69
  item.model = YetAnotherModel
61
70
  item.cron = "5 6 * * *"
62
71
  item.queue = "default"
63
72
  item.timestamp_field = :created_at
64
73
  item.archive_items_older_than = -> { 1.week.ago }
74
+ item.remote_storage = Tartarus::RemoteStorage::Glacier.new(glacier_configuration)
65
75
  end
66
76
 
67
77
  tartarus.schedule # this method must be called to create jobs for sidekiq-cron!
@@ -80,6 +90,86 @@ You can use the following config params:
80
90
  - `timestamp_field` - required, used for performing a query using the value from `archive_items_older_than`
81
91
  - `archive_with` - optional (defaults to `delete_all`). Could be `delete_all`, `destroy_all`, `delete_all_without_batches`, `destroy_all_without_batches`, `delete_all_using_limit_in_batches`
82
92
  - `batch_size` - optional (defaults to `10_000`, used with `delete_all_using_limit_in_batches` strategy)
93
+ - `remote_storage` - optional (defaults to `Tartarus::RemoteStorage::Null` which does nothing). Use this option if you want store the data somewhere before deleting it.
94
+
95
+ ### Remote Storage
96
+
97
+ Currently, only `Glacier` (for AWS Glacier) is supported. Also, it works only with Postgres database and requires [postgres-copy](https://github.com/diogob/postgres-copy).
98
+
99
+ To take advantage of this feature you will need a couple of things:
100
+ 1. Apply `acts_as_copy_target` to the archivable model (from `postgres-copy` gem).
101
+ 2. Create a model that will be used as a registry for all uploads that happened.
102
+
103
+ If you want to make `Version` model archivable and use `ArchiveRegistry` as the registry, you will need the following models and tables:
104
+
105
+ ``` rb
106
+ database.create_table(:archive_registries) do |t|
107
+ t.string :glacier_location, null: false
108
+ t.string :glacier_checksum, null: false
109
+ t.string :glacier_archive_id, null: false
110
+ t.string :archivable_model, null: false
111
+ t.string :tenant_id_field
112
+ t.string :tenant_id
113
+ t.datetime :completed_at, null: false
114
+ end
115
+
116
+ database.create_table(:versions) do |t|
117
+ end
118
+
119
+ class Version < ApplicationRecord
120
+ acts_as_copy_target
121
+ end
122
+
123
+ class ArchiveRegistry < ApplicationRecord
124
+ end
125
+ ```
126
+
127
+ You can use the above schema for the registry model as it contains all needed fields.
128
+
129
+ To initialize the service:
130
+
131
+ ``` rb
132
+ glacier_configuration = Tartarus::RemoteStorage::Glacier::Configuration.build(
133
+ aws_key: ENV.fetch("AWS_KEY"),
134
+ aws_secret: ENV.fetch("AWS_SECRET"),
135
+ aws_region: ENV.fetch("AWS_REGION"),
136
+ vault_name: ENV.fetch("GLACIER_VAULT_NAME"),
137
+ root_path: Rails.root.to_s,
138
+ archive_registry_factory: ArchiveRegistry,
139
+ )
140
+ Tartarus::RemoteStorage::Glacier.new(glacier_configuration)
141
+ ```
142
+
143
+ You can also pass `account_id` (by default "-" string will be used):
144
+
145
+ ``` rb
146
+ glacier_configuration = Tartarus::RemoteStorage::Glacier::Configuration.build(
147
+ aws_key: ENV.fetch("AWS_KEY"),
148
+ aws_secret: ENV.fetch("AWS_SECRET"),
149
+ aws_region: ENV.fetch("AWS_REGION"),
150
+ vault_name: ENV.fetch("GLACIER_VAULT_NAME"),
151
+ root_path: Rails.root.to_s,
152
+ archive_registry_factory: ArchiveRegistry,
153
+ account_id: "some_account_id"
154
+ )
155
+ Tartarus::RemoteStorage::Glacier.new(glacier_configuration)
156
+ ```
157
+
158
+ If you know what you are doing, you can add your own storage, as long as it complies with the following interface:
159
+
160
+ ``` rb
161
+ class Glacier
162
+ attr_reader :configuration
163
+ private :configuration
164
+
165
+ def initialize(configuration)
166
+ @configuration = configuration
167
+ end
168
+
169
+ def store(collection, archivable_model, tenant_id: nil, tenant_id_field: nil)
170
+ end
171
+ end
172
+ ```
83
173
 
84
174
  ### Testing before actually using it
85
175
 
@@ -88,7 +178,6 @@ You might want to verify that the gem works in the way you expect it to work. Fo
88
178
  1. scheduling/enqueueing: use `Tartarus::ScheduleArchivingModel#schedule` - for example, `Tartarus::ScheduleArchivingModel.new.schedule("PaperTrailVersion")`, it's going to enqueue either `Tartarus::Sidekiq::ArchiveModelWithTenantJob` or `Tartarus::Sidekiq::ArchiveModelWithoutTenantJob`, depending on the config.
89
179
  2. execution of the archiving logic: use `Tartarus::ArchiveModelWithTenant#archive` (for example, `Tartarus::ArchiveModelWithTenant.new.archive("PaperTrailVersion", "User")`) or `Tartarus::ArchiveModelWithoutTenant#archive` (for example, `Tartarus::ArchiveModelWithoutTenant.new.archive("PaperTrailVersion")`)
90
180
 
91
-
92
181
  You might also want to check `spec/integration` to get an idea how the integration tests were written.
93
182
 
94
183
  ## Development
@@ -97,10 +186,6 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
97
186
 
98
187
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
99
188
 
100
- ## TODO
101
-
102
- - add support for uploading archives to AWS Glacier before deleting items
103
-
104
189
  ## Contributing
105
190
 
106
191
  Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/tartarus-rb.
data/lib/tartarus.rb CHANGED
@@ -18,7 +18,16 @@ require "tartarus/rb/version"
18
18
  require "tartarus/registry"
19
19
  require "tartarus/repository"
20
20
  require "tartarus/schedule_archiving_model"
21
+ require "tartarus/remote_storage"
22
+ require "tartarus/remote_storage/null"
23
+ require "tartarus/remote_storage/glacier"
24
+ require "tartarus/remote_storage/glacier/client"
25
+ require "tartarus/remote_storage/glacier/file"
26
+ require "tartarus/remote_storage/glacier/csv_export"
27
+ require "tartarus/remote_storage/glacier/register_upload"
28
+ require "tartarus/remote_storage/glacier/configuration"
21
29
  require "sidekiq/cron/job"
30
+ require "aws-sdk-glacier"
22
31
  require "sidekiq"
23
32
 
24
33
  class Tartarus
@@ -1,7 +1,7 @@
1
1
  class Tartarus::ArchivableItem
2
2
  REQUIRED_ATTRIBUTES_NAMES = %i(model cron queue archive_items_older_than timestamp_field active_job
3
3
  archive_with tenant_value_source).freeze
4
- OPTIONAL_ATTRIBUTES_NAMES = %i(tenants_range tenant_id_field batch_size).freeze
4
+ OPTIONAL_ATTRIBUTES_NAMES = %i(tenants_range tenant_id_field batch_size remote_storage).freeze
5
5
 
6
6
  attr_accessor *(REQUIRED_ATTRIBUTES_NAMES + OPTIONAL_ATTRIBUTES_NAMES)
7
7
 
@@ -67,6 +67,10 @@ class Tartarus::ArchivableItem
67
67
  model.to_s == provided_model_name.to_s
68
68
  end
69
69
 
70
+ def remote_storage
71
+ @remote_storage || Tartarus::RemoteStorage::Null
72
+ end
73
+
70
74
  private
71
75
 
72
76
  def validate_presence
@@ -9,8 +9,10 @@ class Tartarus::ArchiveModelWithTenant
9
9
 
10
10
  def archive(model_name, tenant_id)
11
11
  archivable_item = registry.find_by_model(model_name)
12
-
13
- archivable_item.archive_strategy.call(collection_to_archive(model_name, archivable_item, tenant_id))
12
+ collection = collection_to_archive(model_name, archivable_item, tenant_id)
13
+ archivable_item.remote_storage.store(collection, model_name, tenant_id: tenant_id,
14
+ tenant_id_field: archivable_item.tenant_id_field)
15
+ archivable_item.archive_strategy.call(collection)
14
16
  end
15
17
 
16
18
  private
@@ -9,8 +9,9 @@ class Tartarus::ArchiveModelWithoutTenant
9
9
 
10
10
  def archive(model_name)
11
11
  archivable_item = registry.find_by_model(model_name)
12
-
13
- archivable_item.archive_strategy.call(collection_to_archive(model_name, archivable_item))
12
+ collection = collection_to_archive(model_name, archivable_item)
13
+ archivable_item.remote_storage.store(collection, model_name)
14
+ archivable_item.archive_strategy.call(collection)
14
15
  end
15
16
 
16
17
  private
@@ -1,5 +1,5 @@
1
1
  class Tartarus
2
2
  module Rb
3
- VERSION = "0.3.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
@@ -0,0 +1,2 @@
1
+ module Tartarus::RemoteStorage
2
+ end
@@ -0,0 +1,60 @@
1
+ class Tartarus
2
+ module RemoteStorage
3
+ class Glacier
4
+ attr_reader :configuration, :clock
5
+ private :configuration, :clock
6
+
7
+ def initialize(configuration, clock: Time)
8
+ @configuration = configuration
9
+ @clock = clock
10
+ end
11
+
12
+ def store(collection, archivable_model, tenant_id: nil, tenant_id_field: nil)
13
+ path_to_file = path_to_file_for(archivable_model, tenant_id_field, tenant_id)
14
+ export_to_csv(collection, path_to_file)
15
+ glacier_file = Tartarus::RemoteStorage::Glacier::File.new(::File.new(path_to_file))
16
+ glacier_response = upload(glacier_file)
17
+ register_upload(glacier_response, archivable_model, tenant_id_field, tenant_id)
18
+ ensure
19
+ glacier_file.delete_from_local_storage if glacier_file
20
+ end
21
+
22
+ private
23
+
24
+ def upload(file)
25
+ client.upload_archive(configuration.vault_name, file)
26
+ end
27
+
28
+ def client
29
+ @client ||= begin
30
+ Tartarus::RemoteStorage::Glacier::Client.new(
31
+ key: configuration.aws_key,
32
+ secret: configuration.aws_secret,
33
+ region: configuration.aws_region,
34
+ account_id: configuration.account_id,
35
+ )
36
+ end
37
+ end
38
+
39
+ def export_to_csv(collection, path_to_file)
40
+ Tartarus::RemoteStorage::Glacier::CsvExport
41
+ .new(configuration.storage_directory)
42
+ .export(collection, path_to_file)
43
+ end
44
+
45
+
46
+ def register_upload(glacier_response, archivable_model, tenant_id_field, tenant_id)
47
+ Tartarus::RemoteStorage::Glacier::RegisterUpload.new(configuration.archive_registry_factory).register(
48
+ glacier_response,
49
+ archivable_model,
50
+ tenant_id_field,
51
+ tenant_id
52
+ )
53
+ end
54
+
55
+ def path_to_file_for(archivable_model, tenant_id_field, tenant_id)
56
+ "#{configuration.storage_directory}/#{archivable_model}_#{tenant_id_field}_#{tenant_id}_#{clock.now.to_i}.csv"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Tartarus
4
+ module RemoteStorage
5
+ class Glacier
6
+ class Client
7
+ attr_reader :client, :account_id
8
+ private :client, :account_id
9
+
10
+ def initialize(key:, secret:, region:, account_id:)
11
+ @client = Aws::Glacier::Client.new(credentials: Aws::Credentials.new(key, secret), region: region)
12
+ @account_id = account_id
13
+ end
14
+
15
+ def upload_archive(vault_name, file)
16
+ client.upload_archive(
17
+ account_id: account_id,
18
+ archive_description: file.description,
19
+ body: file.body,
20
+ vault_name: vault_name
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,43 @@
1
+ class Tartarus
2
+ module RemoteStorage
3
+ class Glacier
4
+ class Configuration
5
+ DEFAULT_ACCOUNT_ID = "-"
6
+ private_constant :DEFAULT_ACCOUNT_ID
7
+
8
+ REQUIRED_ATTRIBUTES_NAMES = %i(aws_key aws_secret aws_region account_id vault_name root_path
9
+ archive_registry_factory).freeze
10
+ attr_accessor *REQUIRED_ATTRIBUTES_NAMES
11
+
12
+ def self.build(aws_key:, aws_secret:, aws_region:, account_id: DEFAULT_ACCOUNT_ID, vault_name:, root_path:, archive_registry_factory:)
13
+ new.tap do |config|
14
+ config.aws_key = aws_key
15
+ config.aws_secret = aws_secret
16
+ config.aws_region = aws_region
17
+ config.account_id = account_id
18
+ config.vault_name = vault_name
19
+ config.root_path = root_path
20
+ config.archive_registry_factory = archive_registry_factory
21
+ config.validate!
22
+ end
23
+ end
24
+
25
+ def validate!
26
+ validate_presence
27
+ end
28
+
29
+ def storage_directory
30
+ "#{root_path}/tmp/tartarus/#{archive_registry_factory}"
31
+ end
32
+
33
+ private
34
+
35
+ def validate_presence
36
+ REQUIRED_ATTRIBUTES_NAMES.each do |attribute|
37
+ raise ":#{attribute} must be present" if public_send(attribute).nil?
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,38 @@
1
+ class Tartarus
2
+ module RemoteStorage
3
+ class Glacier
4
+ class CsvExport
5
+ FILE_MODE = "w"
6
+ DELIMITER = ";"
7
+ NO_PATH_FOR_EXPORT = nil
8
+ ENCODING = "UTF-8"
9
+ private_constant :FILE_MODE, :DELIMITER, :NO_PATH_FOR_EXPORT, :ENCODING
10
+
11
+ attr_reader :storage_directory, :file_service, :file_utils
12
+ private :storage_directory, :file_service, :file_utils
13
+
14
+ def initialize(storage_directory, file_service: ::File, file_utils: FileUtils)
15
+ @storage_directory = storage_directory
16
+ @file_service = file_service
17
+ @file_utils = file_utils
18
+ end
19
+
20
+ def export(collection, path_to_file)
21
+ with_csv_export_file(path_to_file) do |file|
22
+ collection.copy_to(NO_PATH_FOR_EXPORT, delimiter: DELIMITER) do |line|
23
+ file.write(line.force_encoding(ENCODING))
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def with_csv_export_file(path_to_file, &block)
31
+ file_utils.mkdir_p(storage_directory) if !file_service.exist?(storage_directory)
32
+
33
+ file_service.open(path_to_file, FILE_MODE, &block)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ require "delegate"
2
+
3
+ class Tartarus
4
+ module RemoteStorage
5
+ class Glacier
6
+ class File < SimpleDelegator
7
+ def description
8
+ file_service.basename(self, ".*")
9
+ end
10
+
11
+ def body
12
+ self
13
+ end
14
+
15
+ def checksum
16
+ Digest::SHA256.file(path)
17
+ end
18
+
19
+ def delete_from_local_storage
20
+ file_service.delete(path)
21
+ end
22
+
23
+ private
24
+
25
+ def file_service
26
+ ::File
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ class Tartarus
2
+ module RemoteStorage
3
+ class Glacier
4
+ class RegisterUpload
5
+ attr_reader :archive_registry_factory, :clock
6
+ private :archive_registry_factory, :clock
7
+
8
+ def initialize(archive_registry_factory, clock: Time)
9
+ @archive_registry_factory = archive_registry_factory
10
+ @clock = clock
11
+ end
12
+
13
+ def register(glacier_response, archivable_model, tenant_id_field, tenant_id)
14
+ archive_registry_factory.new.tap do |archive_registry|
15
+ archive_registry.glacier_location = glacier_response.location
16
+ archive_registry.glacier_checksum = glacier_response.checksum
17
+ archive_registry.glacier_archive_id = glacier_response.archive_id
18
+ archive_registry.archivable_model = archivable_model
19
+ archive_registry.tenant_id_field = tenant_id_field
20
+ archive_registry.tenant_id = tenant_id
21
+ archive_registry.completed_at = clock.now
22
+ archive_registry.save!
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,8 @@
1
+ class Tartarus
2
+ module RemoteStorage
3
+ class Null
4
+ def self.store(*)
5
+ end
6
+ end
7
+ end
8
+ end
data/tartarus-rb.gemspec CHANGED
@@ -29,11 +29,17 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_dependency "sidekiq", ">= 5"
31
31
  spec.add_dependency "sidekiq-cron", "~> 1"
32
+ spec.add_dependency "aws-sdk-glacier"
32
33
 
33
34
  spec.add_development_dependency "rake", "~> 13.0"
34
35
  spec.add_development_dependency "rspec", "~> 3.0"
35
36
  spec.add_development_dependency "rspec-sidekiq"
36
37
 
37
38
  spec.add_development_dependency "activerecord", "~> 6"
38
- spec.add_development_dependency "sqlite3"
39
+ spec.add_development_dependency "pg"
40
+ spec.add_development_dependency "vcr"
41
+ spec.add_development_dependency "webmock"
42
+ spec.add_development_dependency "dotenv"
43
+ spec.add_development_dependency "postgres-copy"
44
+ spec.add_development_dependency "timecop"
39
45
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tartarus-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karol Galanciak
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-08 00:00:00.000000000 Z
11
+ date: 2021-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk-glacier
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -95,7 +109,77 @@ dependencies:
95
109
  - !ruby/object:Gem::Version
96
110
  version: '6'
97
111
  - !ruby/object:Gem::Dependency
98
- name: sqlite3
112
+ name: pg
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: vcr
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: webmock
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: dotenv
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: postgres-copy
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: timecop
99
183
  requirement: !ruby/object:Gem::Requirement
100
184
  requirements:
101
185
  - - ">="
@@ -116,6 +200,7 @@ executables: []
116
200
  extensions: []
117
201
  extra_rdoc_files: []
118
202
  files:
203
+ - ".env.sample"
119
204
  - ".gitignore"
120
205
  - ".rspec"
121
206
  - ".travis.yml"
@@ -144,6 +229,14 @@ files:
144
229
  - lib/tartarus/rb.rb
145
230
  - lib/tartarus/rb/version.rb
146
231
  - lib/tartarus/registry.rb
232
+ - lib/tartarus/remote_storage.rb
233
+ - lib/tartarus/remote_storage/glacier.rb
234
+ - lib/tartarus/remote_storage/glacier/client.rb
235
+ - lib/tartarus/remote_storage/glacier/configuration.rb
236
+ - lib/tartarus/remote_storage/glacier/csv_export.rb
237
+ - lib/tartarus/remote_storage/glacier/file.rb
238
+ - lib/tartarus/remote_storage/glacier/register_upload.rb
239
+ - lib/tartarus/remote_storage/null.rb
147
240
  - lib/tartarus/repository.rb
148
241
  - lib/tartarus/schedule_archiving_model.rb
149
242
  - lib/tartarus/sidekiq.rb