data_porter 2.0.0 → 2.3.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 +4 -4
- data/CHANGELOG.md +41 -0
- data/README.md +7 -1
- data/ROADMAP.md +64 -76
- data/app/assets/javascripts/data_porter/progress_controller.js +3 -3
- data/app/controllers/data_porter/concerns/import_validation.rb +5 -5
- data/app/jobs/data_porter/webhook_job.rb +45 -0
- data/app/views/data_porter/imports/index.html.erb +23 -23
- data/app/views/data_porter/imports/new.html.erb +11 -11
- data/app/views/data_porter/imports/show.html.erb +19 -19
- data/app/views/data_porter/mapping_templates/_form.html.erb +10 -10
- data/app/views/data_porter/mapping_templates/edit.html.erb +2 -2
- data/app/views/data_porter/mapping_templates/index.html.erb +10 -10
- data/app/views/data_porter/mapping_templates/new.html.erb +2 -2
- data/config/locales/en.yml +123 -0
- data/config/locales/fr.yml +123 -0
- data/config/routes.rb +2 -2
- data/lib/data_porter/column_transformer.rb +56 -0
- data/lib/data_porter/components/mapping/column_row.rb +1 -1
- data/lib/data_porter/components/mapping/form.rb +4 -4
- data/lib/data_porter/components/mapping/template_select.rb +1 -1
- data/lib/data_porter/components/preview/results_summary.rb +13 -5
- data/lib/data_porter/components/preview/summary_cards.rb +5 -4
- data/lib/data_porter/components/preview/table.rb +3 -3
- data/lib/data_porter/components/progress/bar.rb +9 -2
- data/lib/data_porter/components/shared/pagination.rb +9 -5
- data/lib/data_porter/components/shared/status_badge.rb +3 -1
- data/lib/data_porter/configuration.rb +3 -1
- data/lib/data_porter/dsl/column.rb +3 -2
- data/lib/data_porter/dsl/webhook.rb +31 -0
- data/lib/data_porter/engine.rb +4 -0
- data/lib/data_porter/orchestrator/importer.rb +1 -0
- data/lib/data_porter/orchestrator/record_builder.rb +2 -1
- data/lib/data_porter/orchestrator.rb +3 -0
- data/lib/data_porter/record_validator.rb +2 -2
- data/lib/data_porter/target.rb +11 -1
- data/lib/data_porter/version.rb +1 -1
- data/lib/data_porter/webhook_notifier.rb +100 -0
- data/lib/data_porter.rb +2 -0
- data/lib/generators/data_porter/install/templates/initializer.rb +5 -0
- data/lib/generators/data_porter/locale/locale_generator.rb +42 -0
- data/mkdocs.yml +98 -0
- metadata +10 -3
- data/bookmarklet.md +0 -217
|
@@ -11,10 +11,11 @@ module DataPorter
|
|
|
11
11
|
|
|
12
12
|
def view_template
|
|
13
13
|
div(class: "dp-summary-cards") do
|
|
14
|
-
card("dp-card--complete", @report.complete_count, "
|
|
15
|
-
card("dp-card--partial", @report.partial_count, "
|
|
16
|
-
card("dp-card--missing", @report.missing_count, "
|
|
17
|
-
card("dp-card--duplicate", @report.duplicate_count,
|
|
14
|
+
card("dp-card--complete", @report.complete_count, I18n.t("data_porter.components.summary_cards.ready"))
|
|
15
|
+
card("dp-card--partial", @report.partial_count, I18n.t("data_porter.components.summary_cards.incomplete"))
|
|
16
|
+
card("dp-card--missing", @report.missing_count, I18n.t("data_porter.components.summary_cards.missing"))
|
|
17
|
+
card("dp-card--duplicate", @report.duplicate_count,
|
|
18
|
+
I18n.t("data_porter.components.summary_cards.duplicates"))
|
|
18
19
|
end
|
|
19
20
|
end
|
|
20
21
|
|
|
@@ -24,10 +24,10 @@ module DataPorter
|
|
|
24
24
|
def render_header
|
|
25
25
|
thead do
|
|
26
26
|
tr do
|
|
27
|
-
th { "
|
|
28
|
-
th { "
|
|
27
|
+
th { I18n.t("data_porter.components.table.line_number") }
|
|
28
|
+
th { I18n.t("data_porter.components.table.status") }
|
|
29
29
|
@columns.each { |col| th { col.label } }
|
|
30
|
-
th { "
|
|
30
|
+
th { I18n.t("data_porter.components.table.errors") }
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
end
|
|
@@ -21,7 +21,7 @@ module DataPorter
|
|
|
21
21
|
|
|
22
22
|
def render_label
|
|
23
23
|
div(class: "dp-progress-label") do
|
|
24
|
-
span(data_data_porter__progress_target: "label") { "
|
|
24
|
+
span(data_data_porter__progress_target: "label") { I18n.t("data_porter.components.progress.waiting") }
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -36,11 +36,18 @@ module DataPorter
|
|
|
36
36
|
def stimulus_attrs
|
|
37
37
|
attrs = {
|
|
38
38
|
data_controller: "data-porter--progress",
|
|
39
|
-
data_data_porter__progress_id_value: @import_id.to_s
|
|
39
|
+
data_data_porter__progress_id_value: @import_id.to_s,
|
|
40
|
+
data_data_porter__progress_labels_value: progress_labels.to_json
|
|
40
41
|
}
|
|
41
42
|
attrs[:data_data_porter__progress_url_value] = @status_url if @status_url
|
|
42
43
|
attrs
|
|
43
44
|
end
|
|
45
|
+
|
|
46
|
+
def progress_labels
|
|
47
|
+
%w[pending extracting_headers parsing importing dry_running processing].index_with do |key|
|
|
48
|
+
I18n.t("data_porter.components.progress.#{key}")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
44
51
|
end
|
|
45
52
|
end
|
|
46
53
|
end
|
|
@@ -24,22 +24,26 @@ module DataPorter
|
|
|
24
24
|
private
|
|
25
25
|
|
|
26
26
|
def render_prev
|
|
27
|
+
label = "\u2190 #{I18n.t("data_porter.components.pagination.previous")}"
|
|
27
28
|
if @page > 1
|
|
28
|
-
a(href: page_url(@page - 1), class: "dp-pagination__btn") {
|
|
29
|
+
a(href: page_url(@page - 1), class: "dp-pagination__btn") { label }
|
|
29
30
|
else
|
|
30
|
-
span(class: "dp-pagination__btn dp-pagination__btn--disabled") {
|
|
31
|
+
span(class: "dp-pagination__btn dp-pagination__btn--disabled") { label }
|
|
31
32
|
end
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
def render_indicator
|
|
35
|
-
span(class: "dp-pagination__info")
|
|
36
|
+
span(class: "dp-pagination__info") do
|
|
37
|
+
I18n.t("data_porter.components.pagination.page_info", page: @page, total: @total_pages)
|
|
38
|
+
end
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
def render_next
|
|
42
|
+
label = "#{I18n.t("data_porter.components.pagination.next")} \u2192"
|
|
39
43
|
if @page < @total_pages
|
|
40
|
-
a(href: page_url(@page + 1), class: "dp-pagination__btn") {
|
|
44
|
+
a(href: page_url(@page + 1), class: "dp-pagination__btn") { label }
|
|
41
45
|
else
|
|
42
|
-
span(class: "dp-pagination__btn dp-pagination__btn--disabled") {
|
|
46
|
+
span(class: "dp-pagination__btn dp-pagination__btn--disabled") { label }
|
|
43
47
|
end
|
|
44
48
|
end
|
|
45
49
|
|
|
@@ -10,7 +10,9 @@ module DataPorter
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def view_template
|
|
13
|
-
span(class: "dp-badge dp-badge--#{@status}")
|
|
13
|
+
span(class: "dp-badge dp-badge--#{@status}") do
|
|
14
|
+
I18n.t("data_porter.components.status_badge.#{@status}", default: @status.capitalize)
|
|
15
|
+
end
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
end
|
|
@@ -13,7 +13,8 @@ module DataPorter
|
|
|
13
13
|
:purge_after,
|
|
14
14
|
:max_file_size,
|
|
15
15
|
:max_records,
|
|
16
|
-
:transaction_mode
|
|
16
|
+
:transaction_mode,
|
|
17
|
+
:webhook_secret
|
|
17
18
|
|
|
18
19
|
def initialize
|
|
19
20
|
@parent_controller = "ActionController::Base"
|
|
@@ -28,6 +29,7 @@ module DataPorter
|
|
|
28
29
|
@max_file_size = 10.megabytes
|
|
29
30
|
@max_records = 10_000
|
|
30
31
|
@transaction_mode = :per_record
|
|
32
|
+
@webhook_secret = nil
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
end
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module DataPorter
|
|
4
4
|
module DSL
|
|
5
|
-
Column = Struct.new(:name, :type, :required, :label, :options, keyword_init: true) do
|
|
6
|
-
def initialize(name:, type: :string, required: false, label: nil, **options)
|
|
5
|
+
Column = Struct.new(:name, :type, :required, :label, :transform, :options, keyword_init: true) do
|
|
6
|
+
def initialize(name:, type: :string, required: false, label: nil, transform: [], **options)
|
|
7
7
|
super(
|
|
8
8
|
name: name.to_sym,
|
|
9
9
|
type: type.to_sym,
|
|
10
10
|
required: required,
|
|
11
11
|
label: label || name.to_s.humanize,
|
|
12
|
+
transform: Array(transform),
|
|
12
13
|
options: options
|
|
13
14
|
)
|
|
14
15
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DataPorter
|
|
4
|
+
module DSL
|
|
5
|
+
VALID_WEBHOOK_EVENTS = %i[started completed failed parsed].freeze
|
|
6
|
+
|
|
7
|
+
Webhook = Struct.new(:url, :events, :headers, :payload, keyword_init: true) do
|
|
8
|
+
def initialize(url:, events: VALID_WEBHOOK_EVENTS, headers: {}, payload: nil)
|
|
9
|
+
validate_url!(url)
|
|
10
|
+
events = events.map(&:to_sym)
|
|
11
|
+
validate_events!(events)
|
|
12
|
+
super
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def validate_url!(url)
|
|
18
|
+
raise ArgumentError, "url is required" if url.nil? || url.to_s.strip.empty?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def validate_events!(events)
|
|
22
|
+
events.each do |event|
|
|
23
|
+
next if VALID_WEBHOOK_EVENTS.include?(event)
|
|
24
|
+
|
|
25
|
+
raise ArgumentError,
|
|
26
|
+
"invalid webhook event: #{event}. Must be one of: #{VALID_WEBHOOK_EVENTS.join(", ")}"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/data_porter/engine.rb
CHANGED
|
@@ -4,6 +4,10 @@ module DataPorter
|
|
|
4
4
|
class Engine < ::Rails::Engine
|
|
5
5
|
isolate_namespace DataPorter
|
|
6
6
|
|
|
7
|
+
initializer "data_porter.i18n" do
|
|
8
|
+
config.i18n.load_path += Dir[root.join("config/locales/**/*.yml")]
|
|
9
|
+
end
|
|
10
|
+
|
|
7
11
|
initializer "data_porter.assets.precompile" do |app|
|
|
8
12
|
if app.config.respond_to?(:assets)
|
|
9
13
|
app.config.assets.precompile += %w[
|
|
@@ -22,7 +22,7 @@ module DataPorter
|
|
|
22
22
|
return unless max
|
|
23
23
|
return if count <= max
|
|
24
24
|
|
|
25
|
-
raise Error, "
|
|
25
|
+
raise Error, I18n.t("data_porter.errors.max_records", count: count, max: max)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def build_record(row, index, columns, validator)
|
|
@@ -30,6 +30,7 @@ module DataPorter
|
|
|
30
30
|
line_number: index + 1,
|
|
31
31
|
data: extract_data(row, columns)
|
|
32
32
|
)
|
|
33
|
+
ColumnTransformer.apply_all(record, columns)
|
|
33
34
|
record = @target.transform(record)
|
|
34
35
|
@target.validate(record)
|
|
35
36
|
validator.validate(record)
|
|
@@ -32,12 +32,14 @@ module DataPorter
|
|
|
32
32
|
records = build_records
|
|
33
33
|
@data_import.update!(records: records, status: :previewing)
|
|
34
34
|
build_report
|
|
35
|
+
WebhookNotifier.notify(@data_import, "import.parsed")
|
|
35
36
|
rescue StandardError => e
|
|
36
37
|
handle_failure(e)
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
def import!
|
|
40
41
|
@data_import.importing!
|
|
42
|
+
WebhookNotifier.notify(@data_import, "import.started")
|
|
41
43
|
results = import_records
|
|
42
44
|
update_import_report(results)
|
|
43
45
|
@target.after_import(results, context: build_context)
|
|
@@ -102,6 +104,7 @@ module DataPorter
|
|
|
102
104
|
)
|
|
103
105
|
@data_import.update!(status: :failed, report: report)
|
|
104
106
|
@broadcaster.failure(error.message)
|
|
107
|
+
WebhookNotifier.notify(@data_import, "import.failed")
|
|
105
108
|
end
|
|
106
109
|
end
|
|
107
110
|
end
|
|
@@ -19,14 +19,14 @@ module DataPorter
|
|
|
19
19
|
def validate_required(record, col, value)
|
|
20
20
|
return unless col.required && value.to_s.strip.empty?
|
|
21
21
|
|
|
22
|
-
record.add_error("
|
|
22
|
+
record.add_error(I18n.t("data_porter.errors.required", label: col.label))
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def validate_type(record, col, value)
|
|
26
26
|
return if value.to_s.strip.empty?
|
|
27
27
|
return if TypeValidator.valid?(value, col.type, col.options)
|
|
28
28
|
|
|
29
|
-
record.add_error("
|
|
29
|
+
record.add_error(I18n.t("data_porter.errors.invalid_type", label: col.label, type: col.type))
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
end
|
data/lib/data_porter/target.rb
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
require_relative "dsl/column"
|
|
4
4
|
require_relative "dsl/param"
|
|
5
5
|
require_relative "dsl/api_config"
|
|
6
|
+
require_relative "dsl/webhook"
|
|
6
7
|
|
|
7
8
|
module DataPorter
|
|
8
9
|
class Target
|
|
9
10
|
class << self
|
|
10
11
|
attr_reader :_label, :_model_name, :_icon, :_sources,
|
|
11
12
|
:_columns, :_csv_mappings, :_dedup_keys, :_json_root,
|
|
12
|
-
:_api_config, :_dry_run_enabled, :_params
|
|
13
|
+
:_api_config, :_dry_run_enabled, :_params, :_webhooks
|
|
13
14
|
|
|
14
15
|
def label(value)
|
|
15
16
|
@_label = value
|
|
@@ -72,6 +73,15 @@ module DataPorter
|
|
|
72
73
|
@_params << DSL::Param.new(name: name, **)
|
|
73
74
|
end
|
|
74
75
|
|
|
76
|
+
def webhooks(&)
|
|
77
|
+
@_webhooks = []
|
|
78
|
+
instance_eval(&)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def webhook(url, **)
|
|
82
|
+
@_webhooks << DSL::Webhook.new(url: url, **)
|
|
83
|
+
end
|
|
84
|
+
|
|
75
85
|
private
|
|
76
86
|
|
|
77
87
|
def auto_register
|
data/lib/data_porter/version.rb
CHANGED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DataPorter
|
|
4
|
+
module WebhookNotifier
|
|
5
|
+
EVENT_MAP = {
|
|
6
|
+
"import.started" => :started,
|
|
7
|
+
"import.completed" => :completed,
|
|
8
|
+
"import.failed" => :failed,
|
|
9
|
+
"import.parsed" => :parsed
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def notify(data_import, event)
|
|
15
|
+
webhooks = resolve_webhooks(data_import)
|
|
16
|
+
return if webhooks.nil? || webhooks.empty?
|
|
17
|
+
|
|
18
|
+
event_sym = EVENT_MAP.fetch(event)
|
|
19
|
+
matching = webhooks.select { |w| w.events.include?(event_sym) }
|
|
20
|
+
return if matching.empty?
|
|
21
|
+
|
|
22
|
+
matching.each do |webhook|
|
|
23
|
+
payload = build_payload(data_import, event, webhook)
|
|
24
|
+
DataPorter::WebhookJob.perform_later(webhook.url, payload, webhook.headers.dup)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def resolve_webhooks(data_import)
|
|
29
|
+
data_import.target_class._webhooks
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def build_payload(data_import, event, webhook)
|
|
33
|
+
data = default_payload(data_import, event)
|
|
34
|
+
data = webhook.payload.call(data_import, event, data) if webhook.payload
|
|
35
|
+
data.to_json
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def default_payload(data_import, event)
|
|
39
|
+
{
|
|
40
|
+
"event" => event,
|
|
41
|
+
"timestamp" => Time.current.iso8601,
|
|
42
|
+
"import" => import_data(data_import, event)
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def import_data(data_import, event)
|
|
47
|
+
base = base_import_data(data_import)
|
|
48
|
+
merge_event_data(base, data_import, event)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def base_import_data(data_import)
|
|
52
|
+
{
|
|
53
|
+
"id" => data_import.id,
|
|
54
|
+
"target_key" => data_import.target_key,
|
|
55
|
+
"source_type" => data_import.source_type
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def merge_event_data(base, data_import, event)
|
|
60
|
+
case event
|
|
61
|
+
when "import.completed" then merge_completed(base, data_import)
|
|
62
|
+
when "import.failed" then merge_failed(base, data_import)
|
|
63
|
+
when "import.parsed" then merge_parsed(base, data_import)
|
|
64
|
+
when "import.started" then merge_started(base, data_import)
|
|
65
|
+
else base
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def merge_completed(base, data_import)
|
|
70
|
+
report = data_import.report
|
|
71
|
+
base.merge(
|
|
72
|
+
"imported_count" => report&.imported_count.to_i,
|
|
73
|
+
"errored_count" => report&.errored_count.to_i
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def merge_failed(base, data_import)
|
|
78
|
+
message = first_error_message(data_import.report)
|
|
79
|
+
base.merge("error_message" => message)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def first_error_message(report)
|
|
83
|
+
errors = report&.error_reports
|
|
84
|
+
errors&.first&.message
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def merge_parsed(base, data_import)
|
|
88
|
+
report = data_import.report
|
|
89
|
+
base.merge(
|
|
90
|
+
"records_count" => report&.records_count.to_i,
|
|
91
|
+
"complete_count" => report&.complete_count.to_i,
|
|
92
|
+
"partial_count" => report&.partial_count.to_i
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def merge_started(base, data_import)
|
|
97
|
+
base.merge("records_count" => data_import.records.size)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
data/lib/data_porter.rb
CHANGED
|
@@ -9,6 +9,7 @@ end
|
|
|
9
9
|
require_relative "data_porter/version"
|
|
10
10
|
require_relative "data_porter/configuration"
|
|
11
11
|
require_relative "data_porter/type_validator"
|
|
12
|
+
require_relative "data_porter/column_transformer"
|
|
12
13
|
require_relative "data_porter/store_models/error"
|
|
13
14
|
require_relative "data_porter/store_models/report"
|
|
14
15
|
require_relative "data_porter/store_models/import_record"
|
|
@@ -17,6 +18,7 @@ require_relative "data_porter/registry"
|
|
|
17
18
|
require_relative "data_porter/sources"
|
|
18
19
|
require_relative "data_porter/record_validator"
|
|
19
20
|
require_relative "data_porter/broadcaster"
|
|
21
|
+
require_relative "data_porter/webhook_notifier"
|
|
20
22
|
require_relative "data_porter/orchestrator"
|
|
21
23
|
require_relative "data_porter/rejects_csv_builder"
|
|
22
24
|
require_relative "data_porter/components"
|
|
@@ -36,4 +36,9 @@ DataPorter.configure do |config|
|
|
|
36
36
|
# Auto-purge completed/failed imports older than this duration.
|
|
37
37
|
# Set to nil to disable auto-purge. Run `rake data_porter:purge` manually or via cron.
|
|
38
38
|
# config.purge_after = 60.days
|
|
39
|
+
|
|
40
|
+
# HMAC-SHA256 secret for signing webhook payloads.
|
|
41
|
+
# When set, every webhook request includes an X-DataPorter-Signature header.
|
|
42
|
+
# Set to nil to disable signing (default).
|
|
43
|
+
# config.webhook_secret = ENV["DATA_PORTER_WEBHOOK_SECRET"]
|
|
39
44
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module DataPorter
|
|
6
|
+
module Generators
|
|
7
|
+
class LocaleGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("../../../../config/locales", __dir__)
|
|
9
|
+
|
|
10
|
+
argument :locale, type: :string, default: "en"
|
|
11
|
+
|
|
12
|
+
def copy_locale_file
|
|
13
|
+
source = source_file
|
|
14
|
+
destination = "config/locales/data_porter.#{locale}.yml"
|
|
15
|
+
|
|
16
|
+
copy_file(source, destination)
|
|
17
|
+
gsub_file(destination, /^#{source_locale}:/, "#{locale}:") unless locale == source_locale
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def show_instructions
|
|
21
|
+
say ""
|
|
22
|
+
say "DataPorter locale file created: config/locales/data_porter.#{locale}.yml", :green
|
|
23
|
+
say ""
|
|
24
|
+
say "Next steps:"
|
|
25
|
+
say " 1. Translate the values in the generated file"
|
|
26
|
+
say " 2. Set your default locale in config/application.rb:"
|
|
27
|
+
say " config.i18n.default_locale = :#{locale}"
|
|
28
|
+
say ""
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def source_file
|
|
34
|
+
File.exist?(File.join(self.class.source_root, "#{locale}.yml")) ? "#{locale}.yml" : "en.yml"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def source_locale
|
|
38
|
+
source_file.delete_suffix(".yml")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/mkdocs.yml
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
site_name: DataPorter
|
|
2
|
+
site_description: A mountable Rails engine for data import workflows
|
|
3
|
+
site_url: https://seryllns.github.io/data_porter
|
|
4
|
+
repo_url: https://github.com/SerylLns/data_porter
|
|
5
|
+
repo_name: SerylLns/data_porter
|
|
6
|
+
edit_uri: edit/main/docs/
|
|
7
|
+
|
|
8
|
+
theme:
|
|
9
|
+
name: material
|
|
10
|
+
palette:
|
|
11
|
+
- media: "(prefers-color-scheme: light)"
|
|
12
|
+
scheme: default
|
|
13
|
+
primary: indigo
|
|
14
|
+
accent: indigo
|
|
15
|
+
toggle:
|
|
16
|
+
icon: material/brightness-7
|
|
17
|
+
name: Switch to dark mode
|
|
18
|
+
- media: "(prefers-color-scheme: dark)"
|
|
19
|
+
scheme: slate
|
|
20
|
+
primary: indigo
|
|
21
|
+
accent: indigo
|
|
22
|
+
toggle:
|
|
23
|
+
icon: material/brightness-4
|
|
24
|
+
name: Switch to light mode
|
|
25
|
+
font:
|
|
26
|
+
text: Inter
|
|
27
|
+
code: JetBrains Mono
|
|
28
|
+
icon:
|
|
29
|
+
repo: fontawesome/brands/github
|
|
30
|
+
logo: material/database-import
|
|
31
|
+
features:
|
|
32
|
+
- navigation.sections
|
|
33
|
+
- navigation.expand
|
|
34
|
+
- navigation.top
|
|
35
|
+
- navigation.indexes
|
|
36
|
+
- navigation.footer
|
|
37
|
+
- search.highlight
|
|
38
|
+
- search.suggest
|
|
39
|
+
- content.code.copy
|
|
40
|
+
- content.code.annotate
|
|
41
|
+
- content.tabs.link
|
|
42
|
+
- toc.follow
|
|
43
|
+
|
|
44
|
+
plugins:
|
|
45
|
+
- search
|
|
46
|
+
- exclude:
|
|
47
|
+
glob:
|
|
48
|
+
- blog/*
|
|
49
|
+
- blog_part_2/*
|
|
50
|
+
- V1_PLAN.md
|
|
51
|
+
- dev_to.md
|
|
52
|
+
- reddit_post.md
|
|
53
|
+
|
|
54
|
+
markdown_extensions:
|
|
55
|
+
- admonition
|
|
56
|
+
- pymdownx.details
|
|
57
|
+
- pymdownx.superfences
|
|
58
|
+
- pymdownx.highlight:
|
|
59
|
+
anchor_linenums: true
|
|
60
|
+
line_spans: __span
|
|
61
|
+
pygments_lang_class: true
|
|
62
|
+
- pymdownx.inlinehilite
|
|
63
|
+
- pymdownx.tabbed:
|
|
64
|
+
alternate_style: true
|
|
65
|
+
- pymdownx.emoji:
|
|
66
|
+
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
|
67
|
+
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
|
68
|
+
- pymdownx.snippets:
|
|
69
|
+
base_path: ["."]
|
|
70
|
+
- attr_list
|
|
71
|
+
- md_in_html
|
|
72
|
+
- tables
|
|
73
|
+
- toc:
|
|
74
|
+
permalink: true
|
|
75
|
+
|
|
76
|
+
extra:
|
|
77
|
+
social:
|
|
78
|
+
- icon: fontawesome/brands/github
|
|
79
|
+
link: https://github.com/SerylLns/data_porter
|
|
80
|
+
- icon: fontawesome/brands/dev
|
|
81
|
+
link: https://dev.to/seryllns
|
|
82
|
+
generator: false
|
|
83
|
+
|
|
84
|
+
extra_css:
|
|
85
|
+
- stylesheets/extra.css
|
|
86
|
+
|
|
87
|
+
nav:
|
|
88
|
+
- Home: index.md
|
|
89
|
+
- Getting Started: getting-started.md
|
|
90
|
+
- Reference:
|
|
91
|
+
- Configuration: CONFIGURATION.md
|
|
92
|
+
- Targets: TARGETS.md
|
|
93
|
+
- Sources: SOURCES.md
|
|
94
|
+
- Column Mapping: MAPPING.md
|
|
95
|
+
- Routes: routes.md
|
|
96
|
+
- Roadmap: ROADMAP.md
|
|
97
|
+
- Changelog: changelog.md
|
|
98
|
+
- Contributing: contributing.md
|
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: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Seryl Lounis
|
|
@@ -129,6 +129,7 @@ files:
|
|
|
129
129
|
- app/jobs/data_porter/extract_headers_job.rb
|
|
130
130
|
- app/jobs/data_porter/import_job.rb
|
|
131
131
|
- app/jobs/data_porter/parse_job.rb
|
|
132
|
+
- app/jobs/data_porter/webhook_job.rb
|
|
132
133
|
- app/models/data_porter/data_import.rb
|
|
133
134
|
- app/models/data_porter/mapping_template.rb
|
|
134
135
|
- app/views/data_porter/imports/index.html.erb
|
|
@@ -139,10 +140,12 @@ files:
|
|
|
139
140
|
- app/views/data_porter/mapping_templates/index.html.erb
|
|
140
141
|
- app/views/data_porter/mapping_templates/new.html.erb
|
|
141
142
|
- app/views/layouts/data_porter/application.html.erb
|
|
142
|
-
-
|
|
143
|
+
- config/locales/en.yml
|
|
144
|
+
- config/locales/fr.yml
|
|
143
145
|
- config/routes.rb
|
|
144
146
|
- lib/data_porter.rb
|
|
145
147
|
- lib/data_porter/broadcaster.rb
|
|
148
|
+
- lib/data_porter/column_transformer.rb
|
|
146
149
|
- lib/data_porter/components.rb
|
|
147
150
|
- lib/data_porter/components/base.rb
|
|
148
151
|
- lib/data_porter/components/mapping/column_row.rb
|
|
@@ -159,6 +162,7 @@ files:
|
|
|
159
162
|
- lib/data_porter/dsl/api_config.rb
|
|
160
163
|
- lib/data_porter/dsl/column.rb
|
|
161
164
|
- lib/data_porter/dsl/param.rb
|
|
165
|
+
- lib/data_porter/dsl/webhook.rb
|
|
162
166
|
- lib/data_porter/engine.rb
|
|
163
167
|
- lib/data_porter/orchestrator.rb
|
|
164
168
|
- lib/data_porter/orchestrator/dry_runner.rb
|
|
@@ -179,13 +183,16 @@ files:
|
|
|
179
183
|
- lib/data_porter/target.rb
|
|
180
184
|
- lib/data_porter/type_validator.rb
|
|
181
185
|
- lib/data_porter/version.rb
|
|
186
|
+
- lib/data_porter/webhook_notifier.rb
|
|
182
187
|
- lib/generators/data_porter/install/install_generator.rb
|
|
183
188
|
- lib/generators/data_porter/install/templates/create_data_porter_imports.rb.erb
|
|
184
189
|
- lib/generators/data_porter/install/templates/create_data_porter_mapping_templates.rb.erb
|
|
185
190
|
- lib/generators/data_porter/install/templates/initializer.rb
|
|
191
|
+
- lib/generators/data_porter/locale/locale_generator.rb
|
|
186
192
|
- lib/generators/data_porter/target/target_generator.rb
|
|
187
193
|
- lib/generators/data_porter/target/templates/target.rb.tt
|
|
188
194
|
- lib/tasks/data_porter.rake
|
|
195
|
+
- mkdocs.yml
|
|
189
196
|
- sig/data_porter.rbs
|
|
190
197
|
homepage: https://github.com/SerylLns/data_porter
|
|
191
198
|
licenses:
|
|
@@ -194,7 +201,7 @@ metadata:
|
|
|
194
201
|
homepage_uri: https://github.com/SerylLns/data_porter
|
|
195
202
|
source_code_uri: https://github.com/SerylLns/data_porter
|
|
196
203
|
changelog_uri: https://github.com/SerylLns/data_porter/blob/main/CHANGELOG.md
|
|
197
|
-
documentation_uri: https://github.
|
|
204
|
+
documentation_uri: https://seryllns.github.io/data_porter/
|
|
198
205
|
bug_tracker_uri: https://github.com/SerylLns/data_porter/issues
|
|
199
206
|
rubygems_mcp_server_uri: https://rubygems.org/gems/data_porter
|
|
200
207
|
rubygems_mfa_required: 'true'
|