data_porter 1.0.2 → 1.1.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 +14 -0
- data/README.md +3 -2
- data/app/controllers/data_porter/concerns/mapping_management.rb +13 -3
- data/app/controllers/data_porter/concerns/scope_management.rb +17 -0
- data/app/controllers/data_porter/imports_controller.rb +11 -3
- data/app/controllers/data_porter/mapping_templates_controller.rb +12 -2
- data/app/models/data_porter/mapping_template.rb +2 -0
- data/lib/data_porter/version.rb +1 -1
- data/lib/generators/data_porter/install/templates/create_data_porter_mapping_templates.rb.erb +2 -0
- data/lib/generators/data_porter/install/templates/initializer.rb +6 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 46a9ae914232272194aaa961ed2619a423e2fcbbe9b5dc4dbdb2762bbcdb7129
|
|
4
|
+
data.tar.gz: 4de6b7f13955136df74552b6788792b3f9e8ba0b201e8b0fa25000e28294e13b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0af51f459c999859b1ef723787998134e96a371bfe25c4e99a086a38cd787e35f4c8b2e58331b5594a1baeb7bc0c8220d0ccd5a9c2738f0cbabc1cc69021a754
|
|
7
|
+
data.tar.gz: 735f5bc4d814f7e641fd8e2d9498487ddf75a5ce784f40439fbcfcdd7a995b131e243538dd261ea5d5b4494148a0035b83b7c5bac924de2f9279f2a019dd6af5
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.1.0] - 2026-02-08
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Scoped imports** -- `config.scope` lambda returns the owner object (any ActiveRecord model) for multi-tenant isolation; used for both storage and filtering; IDOR-safe
|
|
13
|
+
- **Scoped mapping templates** -- Templates are filtered and assigned by owner, consistent with import scope
|
|
14
|
+
- **`ScopeManagement` concern** -- Shared `resolve_owner` logic extracted for both controllers
|
|
15
|
+
- **Polymorphic user on `mapping_templates`** -- Migration template adds `user` reference for template ownership
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- `build_import` uses `resolve_owner` instead of raw `current_user`
|
|
20
|
+
- 413 RSpec examples (up from 402), 0 failures
|
|
21
|
+
|
|
8
22
|
## [1.0.2] - 2026-02-07
|
|
9
23
|
|
|
10
24
|
### Changed
|
data/README.md
CHANGED
|
@@ -31,7 +31,8 @@ Supports CSV, JSON, XLSX, and API sources with a declarative DSL for defining im
|
|
|
31
31
|
- **Per-target source filtering** -- Each target declares its allowed sources, the UI filters accordingly
|
|
32
32
|
- **Import deletion & auto-purge** -- Delete imports from the UI, or schedule `rake data_porter:purge` for automatic cleanup
|
|
33
33
|
- **Reject rows export** -- Download a CSV of failed/errored records with error messages after import
|
|
34
|
-
- **
|
|
34
|
+
- **Scoped imports** -- `config.scope` for multi-tenant isolation; each user only sees their own imports
|
|
35
|
+
- **Security validations** -- File size limit, MIME type check, strong parameter whitelisting, IDOR protection via scope
|
|
35
36
|
- **Safety guards** -- Max records limit (`config.max_records`), configurable transaction mode (`:per_record` or `:all`)
|
|
36
37
|
- **Declarative Target DSL** -- One class per import type, zero boilerplate ([docs](docs/TARGETS.md))
|
|
37
38
|
|
|
@@ -141,7 +142,7 @@ pending -> parsing -> previewing -> importing -> completed
|
|
|
141
142
|
git clone https://github.com/SerylLns/data_porter.git
|
|
142
143
|
cd data_porter
|
|
143
144
|
bin/setup
|
|
144
|
-
bundle exec rspec #
|
|
145
|
+
bundle exec rspec # 405 specs
|
|
145
146
|
bundle exec rubocop # 0 offenses
|
|
146
147
|
```
|
|
147
148
|
|
|
@@ -19,7 +19,8 @@ module DataPorter
|
|
|
19
19
|
def load_templates
|
|
20
20
|
return [] unless defined?(DataPorter::MappingTemplate)
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
scope = scoped_template_base
|
|
23
|
+
scope.for_target(@import.target_key)
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def save_column_mapping
|
|
@@ -31,10 +32,19 @@ module DataPorter
|
|
|
31
32
|
return unless params[:save_template] == "1"
|
|
32
33
|
return unless defined?(DataPorter::MappingTemplate)
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
template = scoped_template_base.find_or_initialize_by(
|
|
35
36
|
target_key: @import.target_key,
|
|
36
37
|
name: params[:template_name].presence || "Default"
|
|
37
|
-
)
|
|
38
|
+
)
|
|
39
|
+
template.user ||= resolve_owner
|
|
40
|
+
template.update!(mapping: permitted_column_mapping)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def scoped_template_base
|
|
44
|
+
owner = resolve_owner
|
|
45
|
+
return DataPorter::MappingTemplate unless owner
|
|
46
|
+
|
|
47
|
+
DataPorter::MappingTemplate.where(user: owner)
|
|
38
48
|
end
|
|
39
49
|
|
|
40
50
|
def permitted_column_mapping
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DataPorter
|
|
4
|
+
module Concerns
|
|
5
|
+
module ScopeManagement
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def resolve_owner
|
|
9
|
+
return unless respond_to?(:current_user, true)
|
|
10
|
+
return unless current_user
|
|
11
|
+
|
|
12
|
+
scope = DataPorter.configuration.scope
|
|
13
|
+
scope ? scope.call(current_user) : current_user
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -5,6 +5,7 @@ module DataPorter
|
|
|
5
5
|
include Concerns::ImportValidation
|
|
6
6
|
include Concerns::MappingManagement
|
|
7
7
|
include Concerns::RecordPagination
|
|
8
|
+
include Concerns::ScopeManagement
|
|
8
9
|
|
|
9
10
|
layout "data_porter/application"
|
|
10
11
|
|
|
@@ -12,7 +13,7 @@ module DataPorter
|
|
|
12
13
|
before_action :load_targets, only: %i[index new create]
|
|
13
14
|
|
|
14
15
|
def index
|
|
15
|
-
@imports =
|
|
16
|
+
@imports = scoped_imports.order(created_at: :desc)
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def new
|
|
@@ -88,7 +89,14 @@ module DataPorter
|
|
|
88
89
|
private
|
|
89
90
|
|
|
90
91
|
def set_import
|
|
91
|
-
@import =
|
|
92
|
+
@import = scoped_imports.find(params[:id])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def scoped_imports
|
|
96
|
+
owner = resolve_owner
|
|
97
|
+
return DataPorter::DataImport.all unless owner
|
|
98
|
+
|
|
99
|
+
DataPorter::DataImport.where(user: owner)
|
|
92
100
|
end
|
|
93
101
|
|
|
94
102
|
def load_targets
|
|
@@ -97,7 +105,7 @@ module DataPorter
|
|
|
97
105
|
|
|
98
106
|
def build_import
|
|
99
107
|
@import = DataPorter::DataImport.new(import_params)
|
|
100
|
-
@import.user =
|
|
108
|
+
@import.user = resolve_owner
|
|
101
109
|
@import.status = :pending
|
|
102
110
|
end
|
|
103
111
|
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module DataPorter
|
|
4
4
|
class MappingTemplatesController < DataPorter.configuration.parent_controller.constantize
|
|
5
|
+
include Concerns::ScopeManagement
|
|
6
|
+
|
|
5
7
|
layout "data_porter/application"
|
|
6
8
|
|
|
7
9
|
before_action :set_template, only: %i[edit update destroy]
|
|
8
10
|
|
|
9
11
|
def index
|
|
10
|
-
@templates =
|
|
12
|
+
@templates = scoped_templates.order(:target_key, :name)
|
|
11
13
|
@grouped = @templates.group_by(&:target_key)
|
|
12
14
|
end
|
|
13
15
|
|
|
@@ -18,6 +20,7 @@ module DataPorter
|
|
|
18
20
|
|
|
19
21
|
def create
|
|
20
22
|
@template = MappingTemplate.new(template_params)
|
|
23
|
+
@template.user = resolve_owner
|
|
21
24
|
|
|
22
25
|
if @template.save
|
|
23
26
|
redirect_to mapping_templates_path
|
|
@@ -48,7 +51,14 @@ module DataPorter
|
|
|
48
51
|
private
|
|
49
52
|
|
|
50
53
|
def set_template
|
|
51
|
-
@template =
|
|
54
|
+
@template = scoped_templates.find(params[:id])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def scoped_templates
|
|
58
|
+
owner = resolve_owner
|
|
59
|
+
return MappingTemplate.all unless owner
|
|
60
|
+
|
|
61
|
+
MappingTemplate.where(user: owner)
|
|
52
62
|
end
|
|
53
63
|
|
|
54
64
|
def template_params
|
data/lib/data_porter/version.rb
CHANGED
|
@@ -26,6 +26,12 @@ DataPorter.configure do |config|
|
|
|
26
26
|
# Enabled source types.
|
|
27
27
|
# config.enabled_sources = %i[csv json xlsx api]
|
|
28
28
|
|
|
29
|
+
# Scope imports per owner (multi-tenant isolation).
|
|
30
|
+
# The lambda receives current_user and returns the owner object.
|
|
31
|
+
# Works with any model: User, Member, Hotel, Organization...
|
|
32
|
+
# config.scope = ->(user) { user }
|
|
33
|
+
# config.scope = ->(user) { user.hotel }
|
|
34
|
+
|
|
29
35
|
# Auto-purge completed/failed imports older than this duration.
|
|
30
36
|
# Set to nil to disable auto-purge. Run `rake data_porter:purge` manually or via cron.
|
|
31
37
|
# config.purge_after = 60.days
|
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: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Seryl Lounis
|
|
@@ -122,6 +122,7 @@ files:
|
|
|
122
122
|
- app/controllers/data_porter/concerns/import_validation.rb
|
|
123
123
|
- app/controllers/data_porter/concerns/mapping_management.rb
|
|
124
124
|
- app/controllers/data_porter/concerns/record_pagination.rb
|
|
125
|
+
- app/controllers/data_porter/concerns/scope_management.rb
|
|
125
126
|
- app/controllers/data_porter/imports_controller.rb
|
|
126
127
|
- app/controllers/data_porter/mapping_templates_controller.rb
|
|
127
128
|
- app/jobs/data_porter/dry_run_job.rb
|