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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +7 -1
  4. data/ROADMAP.md +64 -76
  5. data/app/assets/javascripts/data_porter/progress_controller.js +3 -3
  6. data/app/controllers/data_porter/concerns/import_validation.rb +5 -5
  7. data/app/jobs/data_porter/webhook_job.rb +45 -0
  8. data/app/views/data_porter/imports/index.html.erb +23 -23
  9. data/app/views/data_porter/imports/new.html.erb +11 -11
  10. data/app/views/data_porter/imports/show.html.erb +19 -19
  11. data/app/views/data_porter/mapping_templates/_form.html.erb +10 -10
  12. data/app/views/data_porter/mapping_templates/edit.html.erb +2 -2
  13. data/app/views/data_porter/mapping_templates/index.html.erb +10 -10
  14. data/app/views/data_porter/mapping_templates/new.html.erb +2 -2
  15. data/config/locales/en.yml +123 -0
  16. data/config/locales/fr.yml +123 -0
  17. data/config/routes.rb +2 -2
  18. data/lib/data_porter/column_transformer.rb +56 -0
  19. data/lib/data_porter/components/mapping/column_row.rb +1 -1
  20. data/lib/data_porter/components/mapping/form.rb +4 -4
  21. data/lib/data_porter/components/mapping/template_select.rb +1 -1
  22. data/lib/data_porter/components/preview/results_summary.rb +13 -5
  23. data/lib/data_porter/components/preview/summary_cards.rb +5 -4
  24. data/lib/data_porter/components/preview/table.rb +3 -3
  25. data/lib/data_porter/components/progress/bar.rb +9 -2
  26. data/lib/data_porter/components/shared/pagination.rb +9 -5
  27. data/lib/data_porter/components/shared/status_badge.rb +3 -1
  28. data/lib/data_porter/configuration.rb +3 -1
  29. data/lib/data_porter/dsl/column.rb +3 -2
  30. data/lib/data_porter/dsl/webhook.rb +31 -0
  31. data/lib/data_porter/engine.rb +4 -0
  32. data/lib/data_porter/orchestrator/importer.rb +1 -0
  33. data/lib/data_porter/orchestrator/record_builder.rb +2 -1
  34. data/lib/data_porter/orchestrator.rb +3 -0
  35. data/lib/data_porter/record_validator.rb +2 -2
  36. data/lib/data_porter/target.rb +11 -1
  37. data/lib/data_porter/version.rb +1 -1
  38. data/lib/data_porter/webhook_notifier.rb +100 -0
  39. data/lib/data_porter.rb +2 -0
  40. data/lib/generators/data_porter/install/templates/initializer.rb +5 -0
  41. data/lib/generators/data_porter/locale/locale_generator.rb +42 -0
  42. data/mkdocs.yml +98 -0
  43. metadata +10 -3
  44. 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, "Ready")
15
- card("dp-card--partial", @report.partial_count, "Incomplete")
16
- card("dp-card--missing", @report.missing_count, "Missing")
17
- card("dp-card--duplicate", @report.duplicate_count, "Duplicates")
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 { "Status" }
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 { "Errors" }
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") { "Waiting..." }
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") { "\u2190 Previous" }
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") { "\u2190 Previous" }
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") { "Page #{@page} of #{@total_pages}" }
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") { "Next \u2192" }
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") { "Next \u2192" }
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}") { @status.capitalize }
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
@@ -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[
@@ -45,6 +45,7 @@ module DataPorter
45
45
  def finalize_import(results)
46
46
  @data_import.update!(status: :completed)
47
47
  @broadcaster.success
48
+ WebhookNotifier.notify(@data_import, "import.completed")
48
49
  results
49
50
  end
50
51
 
@@ -22,7 +22,7 @@ module DataPorter
22
22
  return unless max
23
23
  return if count <= max
24
24
 
25
- raise Error, "File contains #{count} records, exceeds maximum of #{max}"
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("#{col.label} is required")
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("#{col.label}: invalid #{col.type}")
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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DataPorter
4
- VERSION = "2.0.0"
4
+ VERSION = "2.3.0"
5
5
  end
@@ -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.0.0
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
- - bookmarklet.md
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.com/SerylLns/data_porter#readme
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'