pg_export 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +9 -13
  3. data/.travis.yml +2 -2
  4. data/CHANGELOG.md +9 -0
  5. data/Gemfile +2 -0
  6. data/README.md +28 -29
  7. data/Rakefile +2 -0
  8. data/bin/console +2 -8
  9. data/bin/pg_export +44 -45
  10. data/lib/pg_export/build_logger.rb +4 -2
  11. data/lib/pg_export/configuration.rb +22 -2
  12. data/lib/pg_export/container.rb +48 -0
  13. data/lib/pg_export/import.rb +7 -0
  14. data/lib/pg_export/lib/pg_export/adapters/bash_adapter.rb +37 -0
  15. data/lib/pg_export/lib/pg_export/adapters/ftp_adapter.rb +67 -0
  16. data/lib/pg_export/lib/pg_export/entities/dump.rb +45 -0
  17. data/lib/pg_export/lib/pg_export/factories/cipher_factory.rb +32 -0
  18. data/lib/pg_export/lib/pg_export/factories/dump_factory.rb +31 -0
  19. data/lib/pg_export/lib/pg_export/factories/ftp_adapter_factory.rb +22 -0
  20. data/lib/pg_export/lib/pg_export/listeners/interactive/build_dump.rb +19 -0
  21. data/lib/pg_export/lib/pg_export/listeners/interactive/close_ftp_connection.rb +19 -0
  22. data/lib/pg_export/lib/pg_export/listeners/interactive/decrypt_dump.rb +19 -0
  23. data/lib/pg_export/lib/pg_export/listeners/interactive/download_dump_from_ftp.rb +19 -0
  24. data/lib/pg_export/lib/pg_export/listeners/interactive/encrypt_dump.rb +19 -0
  25. data/lib/pg_export/lib/pg_export/listeners/interactive/fetch_dumps_from_ftp.rb +19 -0
  26. data/lib/pg_export/lib/pg_export/listeners/interactive/open_ftp_connection.rb +19 -0
  27. data/lib/pg_export/lib/pg_export/listeners/interactive/remove_old_dumps_from_ftp.rb +23 -0
  28. data/lib/pg_export/lib/pg_export/listeners/interactive/restore.rb +19 -0
  29. data/lib/pg_export/lib/pg_export/listeners/interactive/upload_dump_to_ftp.rb +19 -0
  30. data/lib/pg_export/lib/pg_export/listeners/interactive_listener.rb +49 -0
  31. data/lib/pg_export/lib/pg_export/listeners/plain/build_dump.rb +15 -0
  32. data/lib/pg_export/lib/pg_export/listeners/plain/close_ftp_connection.rb +15 -0
  33. data/lib/pg_export/lib/pg_export/listeners/plain/decrypt_dump.rb +15 -0
  34. data/lib/pg_export/lib/pg_export/listeners/plain/download_dump_from_ftp.rb +15 -0
  35. data/lib/pg_export/lib/pg_export/listeners/plain/encrypt_dump.rb +15 -0
  36. data/lib/pg_export/lib/pg_export/listeners/plain/fetch_dumps_from_ftp.rb +15 -0
  37. data/lib/pg_export/lib/pg_export/listeners/plain/open_ftp_connection.rb +15 -0
  38. data/lib/pg_export/lib/pg_export/listeners/plain/remove_old_dumps_from_ftp.rb +17 -0
  39. data/lib/pg_export/lib/pg_export/listeners/plain/restore.rb +15 -0
  40. data/lib/pg_export/lib/pg_export/listeners/plain/upload_dump_to_ftp.rb +15 -0
  41. data/lib/pg_export/lib/pg_export/listeners/plain_listener.rb +17 -0
  42. data/lib/pg_export/lib/pg_export/operations/decrypt_dump.rb +20 -0
  43. data/lib/pg_export/lib/pg_export/operations/encrypt_dump.rb +18 -0
  44. data/lib/pg_export/lib/pg_export/operations/open_ftp_connection.rb +19 -0
  45. data/lib/pg_export/lib/pg_export/operations/remove_old_dumps_from_ftp.rb +22 -0
  46. data/lib/pg_export/lib/pg_export/repositories/ftp_dump_file_repository.rb +19 -0
  47. data/lib/pg_export/lib/pg_export/repositories/ftp_dump_repository.rb +36 -0
  48. data/lib/pg_export/lib/pg_export/transactions/export_dump.rb +56 -0
  49. data/lib/pg_export/lib/pg_export/transactions/import_dump_interactively.rb +67 -0
  50. data/lib/pg_export/lib/pg_export/types.rb +14 -0
  51. data/lib/pg_export/lib/pg_export/ui/interactive/input.rb +36 -0
  52. data/lib/pg_export/lib/pg_export/ui/plain/input.rb +17 -0
  53. data/lib/pg_export/lib/pg_export/value_objects/dump_file.rb +70 -0
  54. data/lib/pg_export/system/boot/config.rb +11 -0
  55. data/lib/pg_export/system/boot/interactive.rb +32 -0
  56. data/lib/pg_export/system/boot/logger.rb +15 -0
  57. data/lib/pg_export/system/boot/plain.rb +33 -0
  58. data/lib/pg_export/version.rb +3 -1
  59. data/lib/pg_export.rb +23 -20
  60. data/pg_export.gemspec +13 -10
  61. metadata +108 -52
  62. data/lib/pg_export/aes/base.rb +0 -47
  63. data/lib/pg_export/aes/decryptor.rb +0 -13
  64. data/lib/pg_export/aes/encryptor.rb +0 -13
  65. data/lib/pg_export/aes.rb +0 -3
  66. data/lib/pg_export/bash/adapter.rb +0 -31
  67. data/lib/pg_export/bash/factory.rb +0 -23
  68. data/lib/pg_export/bash/repository.rb +0 -18
  69. data/lib/pg_export/boot_container.rb +0 -69
  70. data/lib/pg_export/dump.rb +0 -62
  71. data/lib/pg_export/errors.rb +0 -5
  72. data/lib/pg_export/ftp/adapter.rb +0 -41
  73. data/lib/pg_export/ftp/connection.rb +0 -40
  74. data/lib/pg_export/ftp/repository.rb +0 -40
  75. data/lib/pg_export/roles/colourable_string.rb +0 -19
  76. data/lib/pg_export/roles/human_readable.rb +0 -17
  77. data/lib/pg_export/roles/interactive.rb +0 -98
  78. data/lib/pg_export/roles/validatable.rb +0 -24
  79. data/lib/pg_export/services/create_and_export_dump.rb +0 -20
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # auto_register: false
4
+
5
+ require 'dry/transaction'
6
+
7
+ require 'pg_export/import'
8
+ require 'pg_export/container'
9
+
10
+ class PgExport
11
+ module Transactions
12
+ class ImportDumpInteractively
13
+ include Dry::Transaction(container: PgExport::Container)
14
+ include Import[
15
+ 'adapters.bash_adapter',
16
+ 'repositories.ftp_dump_repository',
17
+ 'repositories.ftp_dump_file_repository',
18
+ 'ui_input'
19
+ ]
20
+
21
+ step :open_ftp_connection, with: 'operations.open_ftp_connection'
22
+ step :fetch_dumps_from_ftp
23
+ step :select_dump
24
+ step :download_dump_from_ftp
25
+ step :close_ftp_connection
26
+ step :decrypt_dump, with: 'operations.decrypt_dump'
27
+ step :select_database
28
+ step :restore
29
+
30
+ private
31
+
32
+ def fetch_dumps_from_ftp(database_name:, ftp_adapter:)
33
+ dumps = ftp_dump_repository.all(database_name: database_name, ftp_adapter: ftp_adapter)
34
+ return Failure(message: 'No dumps') if dumps.none?
35
+
36
+ Success(ftp_adapter: ftp_adapter, dumps: dumps)
37
+ end
38
+
39
+ def select_dump(dumps:, ftp_adapter:)
40
+ dump = ui_input.select_dump(dumps)
41
+ Success(dump: dump, ftp_adapter: ftp_adapter)
42
+ end
43
+
44
+ def download_dump_from_ftp(dump:, ftp_adapter:)
45
+ dump.file = ftp_dump_file_repository.by_name(name: dump.name, ftp_adapter: ftp_adapter)
46
+ Success(dump: dump, ftp_adapter: ftp_adapter)
47
+ end
48
+
49
+ def close_ftp_connection(dump:, ftp_adapter:)
50
+ Thread.new { ftp_adapter.close_ftp }
51
+ Success(dump: dump)
52
+ end
53
+
54
+ def select_database(dump:)
55
+ name = ui_input.enter_database_name(dump.database)
56
+ Success(dump: dump, database: name)
57
+ end
58
+
59
+ def restore(dump:, database:)
60
+ bash_adapter.pg_restore(dump.file, database)
61
+ Success({})
62
+ rescue bash_adapter.class::PgRestoreError => e
63
+ Failure(message: e.to_s)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-types'
4
+ require 'pg_export/lib/pg_export/value_objects/dump_file'
5
+
6
+ class PgExport
7
+ module Types
8
+ include Dry::Types.module
9
+
10
+ DumpName = Strict::String.constrained(format: /.+_20[0-9]{6}_[0-9]{6}\Z/)
11
+ DumpType = Types::Coercible::String.enum('plain', 'encrypted')
12
+ DumpFile = Types.Instance(PgExport::ValueObjects::DumpFile)
13
+ end
14
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty-prompt'
4
+
5
+ class PgExport
6
+ module Ui
7
+ module Interactive
8
+ class Input
9
+ def select_dump(dumps)
10
+ idx = prompt.select('Select dump to import:') do |menu|
11
+ menu.enum '.'
12
+ dumps.each_with_index do |d, i|
13
+ menu.choice(d.to_s, i)
14
+ end
15
+ end
16
+
17
+ dumps[idx]
18
+ end
19
+
20
+ def enter_database_name(default = nil)
21
+ puts 'To which database would you like to restore the downloaded dump?'
22
+ prompt.ask('Enter a local database name:') do |q|
23
+ q.required(true)
24
+ q.default(default) if default
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def prompt
31
+ TTY::Prompt.new
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PgExport
4
+ module Ui
5
+ module Plain
6
+ class Input
7
+ def select_dump(dumps)
8
+ dumps[0]
9
+ end
10
+
11
+ def enter_database_name(default)
12
+ default
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+
5
+ class PgExport
6
+ module ValueObjects
7
+ class DumpFile
8
+ def initialize(file = Tempfile.new)
9
+ @file = file
10
+ end
11
+
12
+ def path
13
+ file.path
14
+ end
15
+
16
+ def size
17
+ file.size
18
+ end
19
+
20
+ def rewind
21
+ file.rewind
22
+ end
23
+
24
+ def read
25
+ file.read
26
+ end
27
+
28
+ def copy(cipher:)
29
+ cipher.reset
30
+ new_self = self.class.new
31
+ new_self.write do |f|
32
+ each_chunk do |chunk|
33
+ f << cipher.update(chunk)
34
+ end
35
+ f << cipher.final
36
+ end
37
+
38
+ new_self
39
+ end
40
+
41
+ def write(&block)
42
+ File.open(path, 'w', &block)
43
+ end
44
+
45
+ def each_chunk
46
+ File.open(path, 'r') do |file|
47
+ yield file.read(CHUNK_SIZE) until file.eof?
48
+ end
49
+ end
50
+
51
+ def size_human
52
+ MAPPING.each_pair { |e, s| return "#{(size.to_f / (s / 1024)).round(2)}#{e}" if size < s }
53
+ end
54
+
55
+ private
56
+
57
+ CHUNK_SIZE = (2**16).freeze
58
+ MAPPING = {
59
+ 'B' => 1024,
60
+ 'kB' => 1024 * 1024,
61
+ 'MB' => 1024 * 1024 * 1024,
62
+ 'GB' => 1024 * 1024 * 1024 * 1024,
63
+ 'TB' => 1024 * 1024 * 1024 * 1024 * 1024
64
+ }.freeze
65
+ private_constant :CHUNK_SIZE, :MAPPING
66
+
67
+ attr_reader :file
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ PgExport::Container.boot :config do
4
+ init do
5
+ require 'pg_export/configuration'
6
+ end
7
+
8
+ start do
9
+ register(:config, memoize: true) { PgExport::Configuration.build(ENV) }
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ PgExport::Container.boot(:interactive) do
4
+ init do
5
+ require 'pg_export/lib/pg_export/transactions/import_dump_interactively'
6
+ end
7
+
8
+ start do
9
+ use :main
10
+
11
+ # type = 'plain'
12
+ type = 'interactive'
13
+
14
+ transaction = PgExport::Transactions::ImportDumpInteractively.new(ui_input: target["ui.#{type}.input"])
15
+
16
+ unless target[:config].logger_muted?
17
+ use :logger
18
+
19
+ %i[
20
+ open_ftp_connection
21
+ fetch_dumps_from_ftp
22
+ download_dump_from_ftp
23
+ decrypt_dump
24
+ restore
25
+ ].each do |step|
26
+ transaction.subscribe(step => target["listeners.#{type}.#{step}"])
27
+ end
28
+ end
29
+
30
+ register('transactions.import_dump_interactively', memoize: true) { transaction }
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ PgExport::Container.boot :logger do
4
+ init do
5
+ require 'pg_export/build_logger'
6
+ end
7
+
8
+ start do
9
+ use :config
10
+
11
+ register(:logger, memoize: true) do
12
+ PgExport::BuildLogger.call(stream: $stdout, format: target[:config].logger_format)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ PgExport::Container.boot(:plain) do
4
+ init do
5
+ require 'pg_export/lib/pg_export/transactions/export_dump'
6
+ end
7
+
8
+ start do
9
+ use :main
10
+
11
+ transaction = PgExport::Transactions::ExportDump.new
12
+
13
+ unless target[:config].logger_muted?
14
+ use :logger
15
+
16
+ type = 'plain'
17
+ # type = 'interactive'
18
+
19
+ %i[
20
+ build_dump
21
+ encrypt_dump
22
+ open_ftp_connection
23
+ upload_dump_to_ftp
24
+ remove_old_dumps_from_ftp
25
+ close_ftp_connection
26
+ ].each do |step|
27
+ transaction.subscribe(step => target["listeners.#{type}.#{step}"])
28
+ end
29
+ end
30
+
31
+ register('transactions.export_dump', memoize: true) { transaction }
32
+ end
33
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class PgExport
2
- VERSION = '0.6.1'.freeze
4
+ VERSION = '0.7.0'
3
5
  end
data/lib/pg_export.rb CHANGED
@@ -1,29 +1,32 @@
1
- require 'pg_export/version'
2
- require 'pg_export/configuration'
3
- require 'pg_export/boot_container'
4
- require 'pg_export/roles/interactive'
5
- require 'pg_export/errors'
6
- require 'pg_export/roles/validatable'
1
+ # frozen_string_literal: true
2
+
3
+ require 'pry'
4
+ require 'pg_export/container'
7
5
 
8
6
  class PgExport
9
- include Roles::Validatable
10
-
11
- def initialize(**args)
12
- config = Configuration.new(**args)
13
- extend Roles::Interactive if config.interactive
14
- @container = BootContainer.call(config.to_h)
15
- rescue Dry::Struct::Error => e
16
- raise ArgumentError, e
7
+ class InitializationError < StandardError; end
8
+
9
+ class << self
10
+ def interactive
11
+ PgExport::Container.start(:interactive)
12
+ new(transaction: PgExport::Container['transactions.import_dump_interactively'])
13
+ end
14
+
15
+ def plain
16
+ PgExport::Container.start(:plain)
17
+ new(transaction: PgExport::Container['transactions.export_dump'])
18
+ end
19
+ end
20
+
21
+ def initialize(transaction:)
22
+ @transaction = transaction
17
23
  end
18
24
 
19
- def call(database_name, keep_dumps)
20
- container[:create_and_export_dump].call(
21
- validate_database_name(database_name),
22
- validate_keep_dumps(keep_dumps)
23
- )
25
+ def call(database_name, &block)
26
+ transaction.call(database_name: database_name, &block)
24
27
  end
25
28
 
26
29
  private
27
30
 
28
- attr_reader :container
31
+ attr_reader :transaction
29
32
  end
data/pg_export.gemspec CHANGED
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'pg_export/version'
5
6
 
@@ -18,16 +19,18 @@ Gem::Specification.new do |spec|
18
19
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
20
  spec.executables = ['pg_export']
20
21
  spec.require_paths = ['lib']
21
- spec.required_ruby_version = '>= 2.2.0'
22
+ spec.required_ruby_version = '>= 2.3.0'
22
23
 
23
- spec.add_dependency 'cli_spinnable', '~> 0.2'
24
- spec.add_dependency 'dry-types', '~> 0.11.1'
25
- spec.add_dependency 'dry-struct', '~> 0.3.1'
24
+ spec.add_dependency 'dry-initializer', '~> 2.5.0'
25
+ spec.add_dependency 'dry-system', '~> 0.10.0'
26
+ spec.add_dependency 'dry-transaction', '~> 0.13.0'
27
+ spec.add_dependency 'tty-prompt'
28
+ spec.add_dependency 'tty-spinner'
26
29
 
27
- spec.add_development_dependency 'bundler', '~> 1.10'
28
- spec.add_development_dependency 'rubocop', '~> 0.44'
30
+ spec.add_development_dependency 'bundler', '~> 1.16'
31
+ spec.add_development_dependency 'pg', '~> 0.21'
32
+ spec.add_development_dependency 'pry'
29
33
  spec.add_development_dependency 'rake', '~> 10.0'
30
34
  spec.add_development_dependency 'rspec', '~> 3.4'
31
- spec.add_development_dependency 'pg', '~> 0.19'
32
- spec.add_development_dependency 'pry'
35
+ spec.add_development_dependency 'rubocop', '~> 0.59.2'
33
36
  end