forest_liana 9.22.3 → 9.22.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 277b2499887f0ff82ea53ca55fc79843541cef4709f064c70f5efa55a4735d53
4
- data.tar.gz: f0fbeb9771dae6c1db93581279e433815f47eca0e257543f24bf012057da51d3
3
+ metadata.gz: f8365b7c92d4157a34acd7f00af2c3d515976b96884eef48c0a0e72c775b1ea0
4
+ data.tar.gz: a61cfdd476921b56e293014854693c3aa39ffd6d8474544273be222f91c5d0db
5
5
  SHA512:
6
- metadata.gz: 8e02b44b8348a306b0e7e60b44da087c0effb4ba538947e9165e6db2e156698b4a276a0288335ea8b7768454e0d4309ef4232e37c994abd2adffc04763f66f2b
7
- data.tar.gz: 2be7bc1df7f51619407f1fe8f2b49a8e7b5629ada6151677e6f1f9a57a5548ffbeec5cfdb33f31cb8ca015a26937fb73e96f7decb9b63e959903e69497791f1b
6
+ metadata.gz: 8ee81334205b47404f4fc9349fcde08b8d38be877a525a1cfb8bc1481de1b435d88b0724d48552cbc111735a751b2f84c60a49cc4142a5a7c3a9fab6df201d49
7
+ data.tar.gz: dec06373edbb86308bb9577c0f20d61a747ad9fa0fac691b1f70fadada3d2e27406337b188eda61f10c5ad96405c6d65db77bd4e5144dd079e9445d1b1a13cc4
@@ -110,15 +110,16 @@ module ForestLiana
110
110
  private
111
111
 
112
112
  def sort_array_of_objects(array)
113
- array.sort! do |element1, element2|
114
- [element1['type'], element1['id']] <=> [element2['type'], element2['id']]
115
- end
113
+ array.sort_by! { |element| [element['type'].to_s, element['id'].to_s] }
116
114
  end
117
115
 
116
+ # NOTICE: A field 'type' is a String ('String'), an Array (['String'] for an array column)
117
+ # or a Hash (a nested type), and those are not comparable with one another:
118
+ # `['String'] <=> 'String'` returns nil, which makes `sort` raise an ArgumentError as soon
119
+ # as two fields share a name. Only the tie-breaker ever needs to be total, so JSON-ifying
120
+ # that one alone keeps the primary key byte-for-byte identical and the apimap churn-free.
118
121
  def sort_array_of_fields(array)
119
- array.sort do |field1, field2|
120
- [field1['field'], field1['type']] <=> [field2['field'], field2['type']]
121
- end
122
+ array.sort_by { |field| [field['field'].to_s, field['type'].to_json] }
122
123
  end
123
124
 
124
125
  def reorder_keys_basic(object)
@@ -1,3 +1,3 @@
1
1
  module ForestLiana
2
- VERSION = "9.22.3"
2
+ VERSION = "9.22.4"
3
3
  end
@@ -171,6 +171,80 @@ module ForestLiana
171
171
  expect(apimap_sorted['meta']['stack'].keys).to eq(['database_type', 'orm_version'])
172
172
  end
173
173
  end
174
+
175
+ context 'on an apimap with fields sharing a name but not a comparable type' do
176
+ # NOTICE: a field 'type' is a String ('String'), an Array (['String'] for an array
177
+ # column) or a Hash (a nested type). The sort used to compare those values
178
+ # directly, so `['String'] <=> 'String'` returned nil and `sort` raised
179
+ # `ArgumentError: comparison of Hash with Hash failed`. The rescue in `perform`
180
+ # swallowed it and returned a half-processed apimap: collections that were never
181
+ # reached kept attributes outside KEYS_COLLECTION (such as 'search_fields'), and
182
+ # the Forest API rejects those with `HTTP 400 ValidationFailedError`.
183
+ let(:apimap) do
184
+ ActiveSupport::JSON.decode(ActiveSupport::JSON.encode({
185
+ 'meta': {
186
+ stack: {
187
+ 'orm_version': '4.34.9',
188
+ 'database_type': 'postgresql',
189
+ },
190
+ liana: 'forest-rails',
191
+ 'liana_version': '1.5.24',
192
+ },
193
+ 'data': [{
194
+ id: 'posts',
195
+ type: 'collections',
196
+ attributes: {
197
+ name: 'posts',
198
+ fields: [
199
+ { field: 'tags', type: ['String'] },
200
+ { field: 'tags', type: 'String' },
201
+ { field: 'meta', type: { fields: [{ field: 'locale', type: 'String' }] } },
202
+ { field: 'meta', type: 'String' },
203
+ { field: 'id', type: 'Number' },
204
+ ],
205
+ }
206
+ }, {
207
+ id: 'zebras',
208
+ type: 'collections',
209
+ attributes: {
210
+ 'search_fields': ['name'],
211
+ fields: [
212
+ { field: 'id', type: 'Number' },
213
+ ],
214
+ name: 'zebras',
215
+ }
216
+ }]
217
+ }))
218
+ end
219
+
220
+ let(:apimap_sorted) { ApimapSorter.new(apimap).perform }
221
+
222
+ it 'should reorder the whole apimap without bailing out' do
223
+ expect(FOREST_LOGGER).not_to receive(:warn)
224
+
225
+ apimap_sorted
226
+ end
227
+
228
+ it 'should sort the fields of the offending collection' do
229
+ expect(apimap_sorted['data'][0]['attributes']['fields'].map { |field| field['field'] })
230
+ .to eq(['id', 'meta', 'meta', 'tags', 'tags'])
231
+ end
232
+
233
+ it 'should keep a deterministic order between the non-comparable types' do
234
+ tag_types = apimap_sorted['data'][0]['attributes']['fields']
235
+ .select { |field| field['field'] == 'tags' }
236
+ .map { |field| field['type'] }
237
+ expect(tag_types).to eq(['String', ['String']])
238
+ end
239
+
240
+ it 'should keep processing the collections after the offending one' do
241
+ expect(apimap_sorted['data'][1]['attributes'].keys).to eq(['name', 'fields'])
242
+ end
243
+
244
+ it 'should not leak collection attributes that are not part of the schema' do
245
+ expect(apimap_sorted['data'][1]['attributes']).not_to have_key('search_fields')
246
+ end
247
+ end
174
248
  end
175
249
 
176
250
  describe 'a smart field dependencies key' do
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.22.3
4
+ version: 9.22.4
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-09-25 00:00:00.000000000 Z
11
+ date: 2026-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails