forest_liana 9.17.1 → 9.17.2
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/app/services/forest_liana/base_getter.rb +2 -0
- data/app/services/forest_liana/belongs_to_updater.rb +3 -1
- data/app/services/forest_liana/has_many_associator.rb +4 -2
- data/app/services/forest_liana/has_many_dissociator.rb +4 -2
- data/app/services/forest_liana/has_many_getter.rb +6 -6
- data/app/services/forest_liana/record_findable.rb +27 -0
- data/app/services/forest_liana/resource_getter.rb +1 -1
- data/app/services/forest_liana/resource_updater.rb +3 -1
- data/lib/forest_liana/version.rb +1 -1
- data/spec/services/forest_liana/composite_primary_keys_spec.rb +223 -0
- metadata +5 -3
- data/app/services/forest_liana/utils/composite_primary_key_helper.rb +0 -27
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 44ec79f4e42ec91b8c5306d87672aab03a6b30072e88e27fdcb311d3d7d2c8de
|
|
4
|
+
data.tar.gz: e5f37934ccdbf40a78499fd22fcafcd037b8eed1b12034c773bbb499d16b60c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 93d789834be64fe90cdc9b58c8201df5b75396d627a1138ec1d46e437cfb60d7d63246ece332db3890a6a0cb5eb672683655abc5736dc5c85d38ab2aea47e41a
|
|
7
|
+
data.tar.gz: 91b3c9bf5856f4b628ca31b4140bab3f00b96bd3a7afe670cefaa8701499a86e2fca9e0c431b7a621c86283bae0eefa13afb5104f25ad89589105eb007fa34b9
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module ForestLiana
|
|
2
2
|
class BelongsToUpdater
|
|
3
|
+
include ForestLiana::RecordFindable
|
|
4
|
+
|
|
3
5
|
attr_accessor :errors
|
|
4
6
|
|
|
5
7
|
def initialize(resource, association, params)
|
|
@@ -12,7 +14,7 @@ module ForestLiana
|
|
|
12
14
|
|
|
13
15
|
def perform
|
|
14
16
|
begin
|
|
15
|
-
@record = @resource
|
|
17
|
+
@record = find_record(@resource, @resource, @params[:id])
|
|
16
18
|
if (SchemaUtils.polymorphic?(@association))
|
|
17
19
|
if @data.nil?
|
|
18
20
|
new_value = nil
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module ForestLiana
|
|
2
2
|
class HasManyAssociator
|
|
3
|
+
include ForestLiana::RecordFindable
|
|
4
|
+
|
|
3
5
|
def initialize(resource, association, params)
|
|
4
6
|
@resource = resource
|
|
5
7
|
@association = association
|
|
@@ -8,8 +10,8 @@ module ForestLiana
|
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
def perform
|
|
11
|
-
@record = @resource
|
|
12
|
-
associated_records = @
|
|
13
|
+
@record = find_record(@resource, @resource, @params[:id])
|
|
14
|
+
associated_records = @record.send(@association.name)
|
|
13
15
|
|
|
14
16
|
if @data.is_a?(Array)
|
|
15
17
|
@data.each do |record_added|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module ForestLiana
|
|
2
2
|
class HasManyDissociator
|
|
3
|
+
include ForestLiana::RecordFindable
|
|
4
|
+
|
|
3
5
|
def initialize(resource, association, params, forest_user)
|
|
4
6
|
@resource = resource
|
|
5
7
|
@association = association
|
|
@@ -10,8 +12,8 @@ module ForestLiana
|
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
def perform
|
|
13
|
-
@record = @resource
|
|
14
|
-
associated_records = @
|
|
15
|
+
@record = find_record(@resource, @resource, @params[:id])
|
|
16
|
+
associated_records = @record.send(@association.name)
|
|
15
17
|
|
|
16
18
|
remove_association = !@with_deletion || @association.macro == :has_and_belongs_to_many
|
|
17
19
|
if @data.is_a?(Array)
|
|
@@ -29,6 +29,9 @@ module ForestLiana
|
|
|
29
29
|
|
|
30
30
|
if association_class.primary_key.is_a?(Array)
|
|
31
31
|
adapter_name = association_class.connection.adapter_name.downcase
|
|
32
|
+
pk_columns = association_class.primary_key.map do |pk|
|
|
33
|
+
"#{association_class.table_name}.#{pk}"
|
|
34
|
+
end.join(', ')
|
|
32
35
|
|
|
33
36
|
if adapter_name.include?('sqlite')
|
|
34
37
|
# For SQLite: concatenate columns for DISTINCT count
|
|
@@ -37,12 +40,9 @@ module ForestLiana
|
|
|
37
40
|
end.join(" || '|' || ")
|
|
38
41
|
|
|
39
42
|
@records_count = @records.distinct.count(Arel.sql(pk_concat))
|
|
43
|
+
elsif adapter_name.include?('postgresql')
|
|
44
|
+
@records_count = @records.distinct.count(Arel.sql("ROW(#{pk_columns})"))
|
|
40
45
|
else
|
|
41
|
-
# For PostgreSQL/MySQL: use DISTINCT with multiple columns
|
|
42
|
-
pk_columns = association_class.primary_key.map do |pk|
|
|
43
|
-
"#{association_class.table_name}.#{pk}"
|
|
44
|
-
end.join(', ')
|
|
45
|
-
|
|
46
46
|
@records_count = @records.distinct.count(Arel.sql(pk_columns))
|
|
47
47
|
end
|
|
48
48
|
else
|
|
@@ -94,7 +94,7 @@ module ForestLiana
|
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
def prepare_query
|
|
97
|
-
parent_record =
|
|
97
|
+
parent_record = find_record(get_resource(), @resource, @params[:id])
|
|
98
98
|
association = parent_record.send(@params[:association_name])
|
|
99
99
|
@records = optimize_record_loading(association, @search_query_builder.perform(association))
|
|
100
100
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module ForestLiana
|
|
2
|
+
module RecordFindable
|
|
3
|
+
private
|
|
4
|
+
|
|
5
|
+
def find_record(scope, resource, id)
|
|
6
|
+
primary_key = resource.primary_key
|
|
7
|
+
|
|
8
|
+
if primary_key.is_a?(Array)
|
|
9
|
+
id_values = parse_composite_id(id)
|
|
10
|
+
conditions = primary_key.zip(id_values).to_h
|
|
11
|
+
scope.find_by(conditions)
|
|
12
|
+
else
|
|
13
|
+
scope.find(id)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def parse_composite_id(id)
|
|
18
|
+
return id if id.is_a?(Array)
|
|
19
|
+
|
|
20
|
+
if id.to_s.start_with?('[') && id.to_s.end_with?(']')
|
|
21
|
+
JSON.parse(id.to_s)
|
|
22
|
+
else
|
|
23
|
+
raise ForestLiana::Errors::HTTP422Error.new("Composite primary key ID must be in format [value1,value2], received: #{id}")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -14,7 +14,7 @@ module ForestLiana
|
|
|
14
14
|
def perform
|
|
15
15
|
records = optimize_record_loading(@resource, get_resource())
|
|
16
16
|
scoped_records = ForestLiana::ScopeManager.apply_scopes_on_records(records, @user, @collection_name, @params[:timezone])
|
|
17
|
-
@record =
|
|
17
|
+
@record = find_record(scoped_records, @resource, @params[:id])
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module ForestLiana
|
|
2
2
|
class ResourceUpdater
|
|
3
|
+
include ForestLiana::RecordFindable
|
|
4
|
+
|
|
3
5
|
attr_accessor :record
|
|
4
6
|
attr_accessor :errors
|
|
5
7
|
|
|
@@ -14,7 +16,7 @@ module ForestLiana
|
|
|
14
16
|
begin
|
|
15
17
|
collection_name = ForestLiana.name_for(@resource)
|
|
16
18
|
scoped_records = ForestLiana::ScopeManager.apply_scopes_on_records(@resource, @user, collection_name, @params[:timezone])
|
|
17
|
-
@record = scoped_records
|
|
19
|
+
@record = find_record(scoped_records, @resource, @params[:id])
|
|
18
20
|
|
|
19
21
|
if has_strong_parameter
|
|
20
22
|
@record.update(resource_params)
|
data/lib/forest_liana/version.rb
CHANGED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
require 'rails_helper'
|
|
2
|
+
|
|
3
|
+
module ForestLiana
|
|
4
|
+
describe 'Composite Primary Keys Support' do
|
|
5
|
+
let(:rendering_id) { 13 }
|
|
6
|
+
let(:user) { { 'id' => '1', 'rendering_id' => rendering_id } }
|
|
7
|
+
let(:scopes) { { 'scopes' => {}, 'team' => { 'id' => '1', 'name' => 'Operations' } } }
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
ForestLiana::ScopeManager.invalidate_scope_cache(rendering_id)
|
|
11
|
+
allow(ForestLiana::ScopeManager).to receive(:fetch_scopes).and_return(scopes)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe RecordFindable do
|
|
15
|
+
# Create a test class that includes the module to test private methods
|
|
16
|
+
let(:test_class) do
|
|
17
|
+
Class.new do
|
|
18
|
+
include ForestLiana::RecordFindable
|
|
19
|
+
# Expose private methods for testing
|
|
20
|
+
public :find_record, :parse_composite_id
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let(:helper) { test_class.new }
|
|
25
|
+
|
|
26
|
+
describe '#parse_composite_id' do
|
|
27
|
+
it 'correctly parses composite ID in JSON format' do
|
|
28
|
+
expect(helper.parse_composite_id('[1,2]')).to eq([1, 2])
|
|
29
|
+
expect(helper.parse_composite_id('[10,20]')).to eq([10, 20])
|
|
30
|
+
expect(helper.parse_composite_id('["a","b"]')).to eq(['a', 'b'])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'returns array as-is when already an array' do
|
|
34
|
+
expect(helper.parse_composite_id([1, 2])).to eq([1, 2])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'raises error for invalid composite ID format' do
|
|
38
|
+
expect {
|
|
39
|
+
helper.parse_composite_id('invalid')
|
|
40
|
+
}.to raise_error(ForestLiana::Errors::HTTP422Error)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe '#find_record' do
|
|
45
|
+
it 'finds record using composite key conditions' do
|
|
46
|
+
mock_resource = double('Resource', primary_key: [:user_id, :island_id])
|
|
47
|
+
mock_scoped_records = double('ScopedRecords')
|
|
48
|
+
mock_record = double('Record')
|
|
49
|
+
|
|
50
|
+
allow(mock_scoped_records).to receive(:find_by)
|
|
51
|
+
.with({ user_id: 1, island_id: 2 })
|
|
52
|
+
.and_return(mock_record)
|
|
53
|
+
|
|
54
|
+
result = helper.find_record(mock_scoped_records, mock_resource, '[1,2]')
|
|
55
|
+
|
|
56
|
+
expect(result).to eq(mock_record)
|
|
57
|
+
expect(mock_scoped_records).to have_received(:find_by).with({ user_id: 1, island_id: 2 })
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'falls back to standard find for simple primary key' do
|
|
61
|
+
mock_resource = double('Resource', primary_key: 'id')
|
|
62
|
+
mock_scoped_records = double('ScopedRecords')
|
|
63
|
+
mock_record = double('Record')
|
|
64
|
+
|
|
65
|
+
allow(mock_scoped_records).to receive(:find).with(123).and_return(mock_record)
|
|
66
|
+
|
|
67
|
+
result = helper.find_record(mock_scoped_records, mock_resource, 123)
|
|
68
|
+
|
|
69
|
+
expect(result).to eq(mock_record)
|
|
70
|
+
expect(mock_scoped_records).to have_received(:find).with(123)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
describe HasManyGetter do
|
|
76
|
+
describe '#count with composite primary key' do
|
|
77
|
+
let(:composite_association_class) do
|
|
78
|
+
mock_class = double('CompositeModel')
|
|
79
|
+
allow(mock_class).to receive(:primary_key).and_return([:user_id, :island_id])
|
|
80
|
+
allow(mock_class).to receive(:table_name).and_return('user_islands')
|
|
81
|
+
allow(mock_class).to receive(:connection).and_return(double('Connection', adapter_name: adapter_name))
|
|
82
|
+
mock_class
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
let(:mock_records) { double('Records') }
|
|
86
|
+
|
|
87
|
+
before do
|
|
88
|
+
allow(mock_records).to receive(:distinct).and_return(mock_records)
|
|
89
|
+
allow(mock_records).to receive(:count).and_return(5)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
context 'with PostgreSQL adapter' do
|
|
93
|
+
let(:adapter_name) { 'PostgreSQL' }
|
|
94
|
+
|
|
95
|
+
it 'uses ROW() syntax for COUNT DISTINCT' do
|
|
96
|
+
getter = HasManyGetter.allocate
|
|
97
|
+
getter.instance_variable_set(:@records, mock_records)
|
|
98
|
+
|
|
99
|
+
allow(getter).to receive(:model_association).and_return(composite_association_class)
|
|
100
|
+
|
|
101
|
+
getter.count
|
|
102
|
+
|
|
103
|
+
expect(mock_records).to have_received(:count) do |arg|
|
|
104
|
+
expect(arg.to_s).to include('ROW(')
|
|
105
|
+
expect(arg.to_s).to include('user_islands.user_id')
|
|
106
|
+
expect(arg.to_s).to include('user_islands.island_id')
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
context 'with MySQL adapter' do
|
|
112
|
+
let(:adapter_name) { 'Mysql2' }
|
|
113
|
+
|
|
114
|
+
it 'uses standard multi-column syntax for COUNT DISTINCT' do
|
|
115
|
+
getter = HasManyGetter.allocate
|
|
116
|
+
getter.instance_variable_set(:@records, mock_records)
|
|
117
|
+
|
|
118
|
+
allow(getter).to receive(:model_association).and_return(composite_association_class)
|
|
119
|
+
|
|
120
|
+
getter.count
|
|
121
|
+
|
|
122
|
+
expect(mock_records).to have_received(:count) do |arg|
|
|
123
|
+
expect(arg.to_s).not_to include('ROW(')
|
|
124
|
+
expect(arg.to_s).to include('user_islands.user_id, user_islands.island_id')
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
context 'with SQLite adapter' do
|
|
130
|
+
let(:adapter_name) { 'SQLite' }
|
|
131
|
+
|
|
132
|
+
it 'uses concatenation syntax for COUNT DISTINCT' do
|
|
133
|
+
getter = HasManyGetter.allocate
|
|
134
|
+
getter.instance_variable_set(:@records, mock_records)
|
|
135
|
+
|
|
136
|
+
allow(getter).to receive(:model_association).and_return(composite_association_class)
|
|
137
|
+
|
|
138
|
+
getter.count
|
|
139
|
+
|
|
140
|
+
expect(mock_records).to have_received(:count) do |arg|
|
|
141
|
+
expect(arg.to_s).to include("||")
|
|
142
|
+
expect(arg.to_s).to include("'|'")
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
describe BelongsToUpdater do
|
|
150
|
+
describe 'with composite primary key parent' do
|
|
151
|
+
it 'uses find_record to find the parent record' do
|
|
152
|
+
composite_model = double('CompositeModel')
|
|
153
|
+
association = double('Association', name: :user, klass: User)
|
|
154
|
+
mock_record = double('Record', save: true)
|
|
155
|
+
allow(mock_record).to receive(:user=)
|
|
156
|
+
|
|
157
|
+
params = { id: '[1,2]', 'data' => { id: '5', type: 'User' } }
|
|
158
|
+
|
|
159
|
+
updater = described_class.new(composite_model, association, params)
|
|
160
|
+
allow(updater).to receive(:find_record).and_return(mock_record)
|
|
161
|
+
allow(User).to receive(:find).and_return(double('User'))
|
|
162
|
+
allow(SchemaUtils).to receive(:polymorphic?).and_return(false)
|
|
163
|
+
|
|
164
|
+
updater.perform
|
|
165
|
+
|
|
166
|
+
expect(updater).to have_received(:find_record)
|
|
167
|
+
.with(composite_model, composite_model, '[1,2]')
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
describe HasManyAssociator do
|
|
173
|
+
describe 'with composite primary key parent' do
|
|
174
|
+
it 'uses find_record to find the parent record' do
|
|
175
|
+
composite_model = double('CompositeModel')
|
|
176
|
+
association = double('Association', name: :trees, klass: Tree)
|
|
177
|
+
mock_record = double('Record')
|
|
178
|
+
mock_associated_records = double('AssociatedRecords')
|
|
179
|
+
|
|
180
|
+
allow(mock_record).to receive(:send).with(:trees).and_return(mock_associated_records)
|
|
181
|
+
allow(mock_associated_records).to receive(:<<)
|
|
182
|
+
|
|
183
|
+
params = { id: '[1,2]', 'data' => [{ id: '5' }] }
|
|
184
|
+
|
|
185
|
+
associator = described_class.new(composite_model, association, params)
|
|
186
|
+
allow(associator).to receive(:find_record).and_return(mock_record)
|
|
187
|
+
allow(Tree).to receive(:find).and_return(double('Tree'))
|
|
188
|
+
|
|
189
|
+
associator.perform
|
|
190
|
+
|
|
191
|
+
expect(associator).to have_received(:find_record)
|
|
192
|
+
.with(composite_model, composite_model, '[1,2]')
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
describe HasManyDissociator do
|
|
198
|
+
describe 'with composite primary key parent' do
|
|
199
|
+
it 'uses find_record to find the parent record' do
|
|
200
|
+
composite_model = double('CompositeModel')
|
|
201
|
+
association = double('Association', name: :trees, klass: Tree, macro: :has_many)
|
|
202
|
+
mock_record = double('Record')
|
|
203
|
+
mock_associated_records = double('AssociatedRecords')
|
|
204
|
+
|
|
205
|
+
allow(mock_record).to receive(:send).with(:trees).and_return(mock_associated_records)
|
|
206
|
+
allow(mock_associated_records).to receive(:delete)
|
|
207
|
+
|
|
208
|
+
params = { id: '[1,2]', 'data' => [{ id: '5' }], delete: 'false' }
|
|
209
|
+
forest_user = user
|
|
210
|
+
|
|
211
|
+
dissociator = described_class.new(composite_model, association, params, forest_user)
|
|
212
|
+
allow(dissociator).to receive(:find_record).and_return(mock_record)
|
|
213
|
+
allow(Tree).to receive(:find).and_return(double('Tree'))
|
|
214
|
+
|
|
215
|
+
dissociator.perform
|
|
216
|
+
|
|
217
|
+
expect(dissociator).to have_received(:find_record)
|
|
218
|
+
.with(composite_model, composite_model, '[1,2]')
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: forest_liana
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 9.17.
|
|
4
|
+
version: 9.17.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sandro Munda
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -311,6 +311,7 @@ files:
|
|
|
311
311
|
- app/services/forest_liana/operator_date_interval_parser.rb
|
|
312
312
|
- app/services/forest_liana/pie_stat_getter.rb
|
|
313
313
|
- app/services/forest_liana/query_stat_getter.rb
|
|
314
|
+
- app/services/forest_liana/record_findable.rb
|
|
314
315
|
- app/services/forest_liana/resource_creator.rb
|
|
315
316
|
- app/services/forest_liana/resource_getter.rb
|
|
316
317
|
- app/services/forest_liana/resource_updater.rb
|
|
@@ -334,7 +335,6 @@ files:
|
|
|
334
335
|
- app/services/forest_liana/stripe_subscriptions_getter.rb
|
|
335
336
|
- app/services/forest_liana/token.rb
|
|
336
337
|
- app/services/forest_liana/utils/beta_schema_utils.rb
|
|
337
|
-
- app/services/forest_liana/utils/composite_primary_key_helper.rb
|
|
338
338
|
- app/services/forest_liana/utils/context_variables.rb
|
|
339
339
|
- app/services/forest_liana/utils/context_variables_injector.rb
|
|
340
340
|
- app/services/forest_liana/value_stat_getter.rb
|
|
@@ -452,6 +452,7 @@ files:
|
|
|
452
452
|
- spec/services/forest_liana/ability/permission/smart_action_checker_spec.rb
|
|
453
453
|
- spec/services/forest_liana/ability/permission_spec.rb
|
|
454
454
|
- spec/services/forest_liana/apimap_sorter_spec.rb
|
|
455
|
+
- spec/services/forest_liana/composite_primary_keys_spec.rb
|
|
455
456
|
- spec/services/forest_liana/filters_parser_spec.rb
|
|
456
457
|
- spec/services/forest_liana/forest_api_requester_spec.rb
|
|
457
458
|
- spec/services/forest_liana/has_many_getter_spec.rb
|
|
@@ -761,6 +762,7 @@ test_files:
|
|
|
761
762
|
- spec/services/forest_liana/ability/permission/smart_action_checker_spec.rb
|
|
762
763
|
- spec/services/forest_liana/ability/permission_spec.rb
|
|
763
764
|
- spec/services/forest_liana/apimap_sorter_spec.rb
|
|
765
|
+
- spec/services/forest_liana/composite_primary_keys_spec.rb
|
|
764
766
|
- spec/services/forest_liana/filters_parser_spec.rb
|
|
765
767
|
- spec/services/forest_liana/forest_api_requester_spec.rb
|
|
766
768
|
- spec/services/forest_liana/has_many_getter_spec.rb
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
module ForestLiana
|
|
2
|
-
module Utils
|
|
3
|
-
module CompositePrimaryKeyHelper
|
|
4
|
-
def self.find_record(scoped_records, resource, id)
|
|
5
|
-
primary_key = resource.primary_key
|
|
6
|
-
|
|
7
|
-
if primary_key.is_a?(Array)
|
|
8
|
-
id_values = parse_composite_id(id)
|
|
9
|
-
conditions = primary_key.zip(id_values).to_h
|
|
10
|
-
scoped_records.find_by(conditions)
|
|
11
|
-
else
|
|
12
|
-
scoped_records.find(id)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def self.parse_composite_id(id)
|
|
17
|
-
return id if id.is_a?(Array)
|
|
18
|
-
|
|
19
|
-
if id.to_s.start_with?('[') && id.to_s.end_with?(']')
|
|
20
|
-
JSON.parse(id.to_s)
|
|
21
|
-
else
|
|
22
|
-
raise ForestLiana::Errors::HTTP422Error.new("Composite primary key ID must be in format [value1,value2], received: #{id}")
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|