data_porter 0.5.0 → 0.9.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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +12 -6
  4. data/app/assets/javascripts/data_porter/import_form_controller.js +126 -0
  5. data/app/assets/stylesheets/data_porter/table.css +45 -0
  6. data/app/controllers/data_porter/concerns/import_validation.rb +47 -0
  7. data/app/controllers/data_porter/concerns/mapping_management.rb +43 -0
  8. data/app/controllers/data_porter/concerns/record_pagination.rb +19 -0
  9. data/app/controllers/data_porter/imports_controller.rb +14 -48
  10. data/app/views/data_porter/imports/index.html.erb +59 -115
  11. data/app/views/data_porter/imports/new.html.erb +51 -2
  12. data/app/views/data_porter/imports/show.html.erb +10 -0
  13. data/app/views/layouts/data_porter/application.html.erb +17 -146
  14. data/docs/CONFIGURATION.md +28 -6
  15. data/docs/ROADMAP.md +28 -0
  16. data/docs/TARGETS.md +54 -3
  17. data/lib/data_porter/components/shared/pagination.rb +53 -0
  18. data/lib/data_porter/components.rb +1 -0
  19. data/lib/data_porter/dsl/param.rb +32 -0
  20. data/lib/data_porter/engine.rb +4 -0
  21. data/lib/data_porter/orchestrator/dry_runner.rb +30 -0
  22. data/lib/data_porter/orchestrator/importer.rb +41 -0
  23. data/lib/data_porter/orchestrator/record_builder.rb +38 -0
  24. data/lib/data_porter/orchestrator.rb +16 -83
  25. data/lib/data_porter/registry.rb +21 -1
  26. data/lib/data_porter/target.rb +17 -1
  27. data/lib/data_porter/version.rb +1 -1
  28. metadata +14 -4
  29. /data/app/{javascript → assets/javascripts}/data_porter/mapping_controller.js +0 -0
  30. /data/app/{javascript → assets/javascripts}/data_porter/progress_controller.js +0 -0
  31. /data/app/{javascript → assets/javascripts}/data_porter/template_form_controller.js +0 -0
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DataPorter
4
+ module Components
5
+ module Shared
6
+ class Pagination < Base
7
+ def initialize(page:, total_pages:, base_url:)
8
+ super()
9
+ @page = page
10
+ @total_pages = total_pages
11
+ @base_url = base_url
12
+ end
13
+
14
+ def view_template
15
+ return if @total_pages <= 1
16
+
17
+ nav(class: "dp-pagination") do
18
+ render_prev
19
+ render_indicator
20
+ render_next
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def render_prev
27
+ if @page > 1
28
+ a(href: page_url(@page - 1), class: "dp-pagination__btn") { "\u2190 Previous" }
29
+ else
30
+ span(class: "dp-pagination__btn dp-pagination__btn--disabled") { "\u2190 Previous" }
31
+ end
32
+ end
33
+
34
+ def render_indicator
35
+ span(class: "dp-pagination__info") { "Page #{@page} of #{@total_pages}" }
36
+ end
37
+
38
+ def render_next
39
+ if @page < @total_pages
40
+ a(href: page_url(@page + 1), class: "dp-pagination__btn") { "Next \u2192" }
41
+ else
42
+ span(class: "dp-pagination__btn dp-pagination__btn--disabled") { "Next \u2192" }
43
+ end
44
+ end
45
+
46
+ def page_url(number)
47
+ separator = @base_url.include?("?") ? "&" : "?"
48
+ "#{@base_url}#{separator}page=#{number}#records"
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -3,6 +3,7 @@
3
3
  require_relative "components/base"
4
4
  require_relative "components/shared/status_badge"
5
5
  require_relative "components/shared/failure_alert"
6
+ require_relative "components/shared/pagination"
6
7
  require_relative "components/preview/table"
7
8
  require_relative "components/preview/summary_cards"
8
9
  require_relative "components/preview/results_summary"
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DataPorter
4
+ module DSL
5
+ VALID_PARAM_TYPES = %i[select text number hidden].freeze
6
+
7
+ Param = Struct.new(:name, :type, :required, :label, :collection, :default, :options, keyword_init: true) do
8
+ def initialize(**attrs)
9
+ validate_type!(attrs[:type] || :text)
10
+ extra = attrs.except(:name, :type, :required, :label, :collection, :default, :options)
11
+ apply_defaults!(attrs, extra)
12
+ super(**attrs.slice(:name, :type, :required, :label, :collection, :default, :options))
13
+ end
14
+
15
+ private
16
+
17
+ def apply_defaults!(attrs, extra)
18
+ attrs[:name] = attrs[:name].to_sym
19
+ attrs[:type] = (attrs[:type] || :text).to_sym
20
+ attrs[:required] = attrs.fetch(:required, false)
21
+ attrs[:label] ||= attrs[:name].to_s.humanize
22
+ attrs[:options] = extra
23
+ end
24
+
25
+ def validate_type!(type)
26
+ return if VALID_PARAM_TYPES.include?(type.to_sym)
27
+
28
+ raise ArgumentError, "invalid param type: #{type}. Must be one of: #{VALID_PARAM_TYPES.join(", ")}"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -10,6 +10,10 @@ module DataPorter
10
10
  data_porter/application.css
11
11
  data_porter/turbo.min.js
12
12
  data_porter/stimulus.min.js
13
+ data_porter/mapping_controller.js
14
+ data_porter/template_form_controller.js
15
+ data_porter/progress_controller.js
16
+ data_porter/import_form_controller.js
13
17
  ]
14
18
  end
15
19
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DataPorter
4
+ class Orchestrator
5
+ module DryRunner
6
+ private
7
+
8
+ def run_dry_run_records
9
+ records = @data_import.records
10
+ importable = records.select(&:importable?)
11
+ context = build_context
12
+
13
+ importable.each do |record|
14
+ dry_run_record(record, context)
15
+ end
16
+
17
+ @data_import.records_will_change!
18
+ @data_import.update!(records: records)
19
+ end
20
+
21
+ def dry_run_record(record, context)
22
+ @target.persist(record, context: context)
23
+ record.dry_run_passed = true
24
+ rescue StandardError => e
25
+ record.dry_run_passed = false
26
+ record.add_error(e.message)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DataPorter
4
+ class Orchestrator
5
+ module Importer
6
+ private
7
+
8
+ def import_records
9
+ importable = @data_import.importable_records
10
+ context = build_context
11
+ results = { created: 0, errored: 0 }
12
+ total = importable.size
13
+
14
+ importable.each_with_index do |record, index|
15
+ persist_record(record, context, results)
16
+ broadcast_progress(index + 1, total)
17
+ end
18
+
19
+ @data_import.update!(status: :completed)
20
+ @broadcaster.success
21
+ results
22
+ end
23
+
24
+ def persist_record(record, context, results)
25
+ @target.persist(record, context: context)
26
+ results[:created] += 1
27
+ rescue StandardError => e
28
+ record.add_error(e.message)
29
+ @target.on_error(record, e, context: context)
30
+ results[:errored] += 1
31
+ end
32
+
33
+ def update_import_report(results)
34
+ report = @data_import.report || StoreModels::Report.new
35
+ report.imported_count = results[:created]
36
+ report.errored_count = results[:errored]
37
+ @data_import.update!(report: report)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DataPorter
4
+ class Orchestrator
5
+ module RecordBuilder
6
+ private
7
+
8
+ def build_records
9
+ source = build_source
10
+ raw_rows = source.fetch
11
+ columns = @target.class._columns || []
12
+ validator = RecordValidator.new(columns)
13
+
14
+ raw_rows.each_with_index.map do |row, index|
15
+ build_record(row, index, columns, validator)
16
+ end
17
+ end
18
+
19
+ def build_record(row, index, columns, validator)
20
+ record = StoreModels::ImportRecord.new(
21
+ line_number: index + 1,
22
+ data: extract_data(row, columns)
23
+ )
24
+ record = @target.transform(record)
25
+ @target.validate(record)
26
+ validator.validate(record)
27
+ record.determine_status!
28
+ record
29
+ end
30
+
31
+ def extract_data(row, columns)
32
+ columns.each_with_object({}) do |col, hash|
33
+ hash[col.name] = row[col.name] || row[col.name.to_s]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,10 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "orchestrator/record_builder"
4
+ require_relative "orchestrator/importer"
5
+ require_relative "orchestrator/dry_runner"
6
+
3
7
  module DataPorter
4
8
  class Orchestrator
9
+ include RecordBuilder
10
+ include Importer
11
+ include DryRunner
12
+
5
13
  def initialize(data_import, content: nil)
6
14
  @data_import = data_import
7
- @target = data_import.target_class.new
15
+ @target = build_target(data_import)
8
16
  @broadcaster = Broadcaster.new(data_import.id)
9
17
  @source_options = { content: content }.compact
10
18
  end
@@ -48,6 +56,13 @@ module DataPorter
48
56
 
49
57
  private
50
58
 
59
+ def build_target(data_import)
60
+ target = data_import.target_class.new
61
+ config = data_import.config || {}
62
+ target.import_params = config["import_params"] || {}
63
+ target
64
+ end
65
+
51
66
  def build_source
52
67
  @data_import.source_class.new(@data_import, **@source_options)
53
68
  end
@@ -58,88 +73,6 @@ module DataPorter
58
73
  @data_import.update!(config: config)
59
74
  end
60
75
 
61
- def build_records
62
- source = build_source
63
- raw_rows = source.fetch
64
- columns = @target.class._columns || []
65
- validator = RecordValidator.new(columns)
66
-
67
- raw_rows.each_with_index.map do |row, index|
68
- build_record(row, index, columns, validator)
69
- end
70
- end
71
-
72
- def build_record(row, index, columns, validator)
73
- record = StoreModels::ImportRecord.new(
74
- line_number: index + 1,
75
- data: extract_data(row, columns)
76
- )
77
- record = @target.transform(record)
78
- @target.validate(record)
79
- validator.validate(record)
80
- record.determine_status!
81
- record
82
- end
83
-
84
- def extract_data(row, columns)
85
- columns.each_with_object({}) do |col, hash|
86
- hash[col.name] = row[col.name] || row[col.name.to_s]
87
- end
88
- end
89
-
90
- def run_dry_run_records
91
- records = @data_import.records
92
- importable = records.select(&:importable?)
93
- context = build_context
94
-
95
- importable.each do |record|
96
- dry_run_record(record, context)
97
- end
98
-
99
- @data_import.records_will_change!
100
- @data_import.update!(records: records)
101
- end
102
-
103
- def dry_run_record(record, context)
104
- @target.persist(record, context: context)
105
- record.dry_run_passed = true
106
- rescue StandardError => e
107
- record.dry_run_passed = false
108
- record.add_error(e.message)
109
- end
110
-
111
- def import_records
112
- importable = @data_import.importable_records
113
- context = build_context
114
- results = { created: 0, errored: 0 }
115
- total = importable.size
116
-
117
- importable.each_with_index do |record, index|
118
- persist_record(record, context, results)
119
- broadcast_progress(index + 1, total)
120
- end
121
-
122
- @data_import.update!(status: :completed)
123
- @broadcaster.success
124
- results
125
- end
126
-
127
- def persist_record(record, context, results)
128
- @target.persist(record, context: context)
129
- results[:created] += 1
130
- rescue StandardError => e
131
- record.add_error(e.message)
132
- @target.on_error(record, e, context: context)
133
- results[:errored] += 1
134
- end
135
-
136
- def update_import_report(results)
137
- report = @data_import.report || StoreModels::Report.new
138
- report.imported_count = results[:created]
139
- report.errored_count = results[:errored]
140
- @data_import.update!(report: report)
141
- end
142
-
143
76
  def build_report
144
77
  summary = @data_import.records_summary
145
78
  report = StoreModels::Report.new(
@@ -21,7 +21,8 @@ module DataPorter
21
21
  key: key,
22
22
  label: klass._label,
23
23
  icon: klass._icon,
24
- sources: klass._sources || DataPorter.configuration.enabled_sources
24
+ sources: klass._sources || DataPorter.configuration.enabled_sources,
25
+ params: serialize_params(klass._params)
25
26
  }
26
27
  end
27
28
  end
@@ -33,6 +34,25 @@ module DataPorter
33
34
  def clear
34
35
  @targets = {}
35
36
  end
37
+
38
+ private
39
+
40
+ def serialize_params(params)
41
+ return [] unless params
42
+
43
+ params.map { |p| serialize_param(p) }
44
+ end
45
+
46
+ def serialize_param(param)
47
+ {
48
+ name: param.name,
49
+ type: param.type,
50
+ required: param.required,
51
+ label: param.label,
52
+ default: param.default,
53
+ collection: param.collection&.call
54
+ }.compact
55
+ end
36
56
  end
37
57
  end
38
58
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "dsl/column"
4
+ require_relative "dsl/param"
4
5
  require_relative "dsl/api_config"
5
6
 
6
7
  module DataPorter
@@ -8,7 +9,7 @@ module DataPorter
8
9
  class << self
9
10
  attr_reader :_label, :_model_name, :_icon, :_sources,
10
11
  :_columns, :_csv_mappings, :_dedup_keys, :_json_root,
11
- :_api_config, :_dry_run_enabled
12
+ :_api_config, :_dry_run_enabled, :_params
12
13
 
13
14
  def label(value)
14
15
  @_label = value
@@ -62,6 +63,15 @@ module DataPorter
62
63
  @_dry_run_enabled = true
63
64
  end
64
65
 
66
+ def params(&)
67
+ @_params = []
68
+ instance_eval(&)
69
+ end
70
+
71
+ def param(name, **)
72
+ @_params << DSL::Param.new(name: name, **)
73
+ end
74
+
65
75
  private
66
76
 
67
77
  def auto_register
@@ -72,6 +82,12 @@ module DataPorter
72
82
  end
73
83
  end
74
84
 
85
+ attr_accessor :import_params
86
+
87
+ def initialize
88
+ @import_params = {}
89
+ end
90
+
75
91
  def transform(record)
76
92
  record
77
93
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DataPorter
4
- VERSION = "0.5.0"
4
+ VERSION = "0.9.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: data_porter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seryl Lounis
@@ -101,7 +101,11 @@ files:
101
101
  - README.md
102
102
  - ROADMAP.md
103
103
  - Rakefile
104
+ - app/assets/javascripts/data_porter/import_form_controller.js
105
+ - app/assets/javascripts/data_porter/mapping_controller.js
106
+ - app/assets/javascripts/data_porter/progress_controller.js
104
107
  - app/assets/javascripts/data_porter/stimulus.min.js
108
+ - app/assets/javascripts/data_porter/template_form_controller.js
105
109
  - app/assets/javascripts/data_porter/turbo.min.js
106
110
  - app/assets/stylesheets/data_porter/alerts.css
107
111
  - app/assets/stylesheets/data_porter/application.css
@@ -115,11 +119,11 @@ files:
115
119
  - app/assets/stylesheets/data_porter/progress.css
116
120
  - app/assets/stylesheets/data_porter/table.css
117
121
  - app/channels/data_porter/import_channel.rb
122
+ - app/controllers/data_porter/concerns/import_validation.rb
123
+ - app/controllers/data_porter/concerns/mapping_management.rb
124
+ - app/controllers/data_porter/concerns/record_pagination.rb
118
125
  - app/controllers/data_porter/imports_controller.rb
119
126
  - app/controllers/data_porter/mapping_templates_controller.rb
120
- - app/javascript/data_porter/mapping_controller.js
121
- - app/javascript/data_porter/progress_controller.js
122
- - app/javascript/data_porter/template_form_controller.js
123
127
  - app/jobs/data_porter/dry_run_job.rb
124
128
  - app/jobs/data_porter/extract_headers_job.rb
125
129
  - app/jobs/data_porter/import_job.rb
@@ -137,6 +141,7 @@ files:
137
141
  - config/routes.rb
138
142
  - docs/CONFIGURATION.md
139
143
  - docs/MAPPING.md
144
+ - docs/ROADMAP.md
140
145
  - docs/SOURCES.md
141
146
  - docs/TARGETS.md
142
147
  - docs/screenshots/index-with-previewing.jpg
@@ -156,12 +161,17 @@ files:
156
161
  - lib/data_porter/components/preview/table.rb
157
162
  - lib/data_porter/components/progress/bar.rb
158
163
  - lib/data_porter/components/shared/failure_alert.rb
164
+ - lib/data_porter/components/shared/pagination.rb
159
165
  - lib/data_porter/components/shared/status_badge.rb
160
166
  - lib/data_porter/configuration.rb
161
167
  - lib/data_porter/dsl/api_config.rb
162
168
  - lib/data_porter/dsl/column.rb
169
+ - lib/data_porter/dsl/param.rb
163
170
  - lib/data_porter/engine.rb
164
171
  - lib/data_porter/orchestrator.rb
172
+ - lib/data_porter/orchestrator/dry_runner.rb
173
+ - lib/data_porter/orchestrator/importer.rb
174
+ - lib/data_porter/orchestrator/record_builder.rb
165
175
  - lib/data_porter/record_validator.rb
166
176
  - lib/data_porter/registry.rb
167
177
  - lib/data_porter/sources.rb