Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

With rails 7.1, these nil values are not persisted #884

Merged
merged 2 commits into from
Feb 14, 2025

Conversation

jrafanie
Copy link
Member

@jrafanie jrafanie commented Jan 31, 2025

UPDATE: @agrare dug in and nerd sniped @Fryguy who figured it out... Struct#as_json changed with rails 7.1 see the discussion.

We now just convert to hash to avoid the change in Struct#as_json in rails 7.1.

Old Description:

support_url, support_email, and support_description don't exist in the response payload in the cassette for these tests, while others have it, and yet previously, these unset fields were set as nil with rails 7.0. It's unclear what changed with rails 7.1 but now these nil value fields aren't persisted in the extra column, so I had to remove them in the tests where these values in the cassettes didn't exist in the response.

collector/cloud_manager.rb:

  def service_offerings
    aws_service_catalog.client.search_products_as_admin.product_view_details
    ...
  end

parser/cloud_manager.rb:

  collector.service_offerings.each do |service_offering|
    persister_service_offering = persister.service_offerings.build(...
      ...
      :extra   => {
        :product_view_summary => service_offering.product_view_summary,
    ...

Here is my Gemfile.lock with rails 7.0 vs. rails 7.1, using the same aws-sdk gems:

diff --git a/Gemfile.lock70 b/Gemfile.lock71
index 5c9dd6c..4c94758 100644
--- a/Gemfile.lock70
+++ b/Gemfile.lock71
@@ -11,6 +11,15 @@ GIT
       manageiq-gems-pending (~> 0)
       manageiq-smartstate (~> 0.2)
 
+GIT
+  remote: https://github.com/ManageIQ/jquery-rjs.git
+  revision: 3a590dfe66cbd90a487da57c4357a82cb3d83992
+  branch: jquery_rjs_0_1_1
+  specs:
+    jquery-rjs (0.1.1.3)
+      jquery-rails
+      rails (>= 3.2)
+
 GIT
   remote: https://github.com/ManageIQ/manageiq-api
   revision: b11cbf9761f8360f89c6b8f53a114cd499af40ea
@@ -20,15 +29,6 @@ GIT
       config
       jbuilder (~> 2.5, != 2.9.0)
 
-GIT
-  remote: https://github.com/ManageIQ/manageiq-automation_engine
-  revision: 037fd1fd54b89b2c4ea97e268936825825b79e17
-  branch: master
-  specs:
-    manageiq-automation_engine (0.1.0)
-      drb
-      rubyzip (~> 2.0.0)
-
 GIT
   remote: https://github.com/ManageIQ/manageiq-consumption
   revision: bad846a1980c0ded1b166b44438e18f2462836c6
@@ -342,6 +342,31 @@ GIT
       uglifier (~> 4.2.0)
       webpacker (~> 2.0.0)
 
+GIT
+  remote: https://github.com/ManageIQ/ovirt_metrics.git
+  revision: 36a99a40d11de98bd0cce011596777f2c85b9455
+  specs:
+    ovirt_metrics (3.2.0)
+      activerecord (>= 7.0)
+      pg
+
+GIT
+  remote: https://github.com/jrafanie/activerecord-virtual_attributes.git
+  revision: afd9619b5093c408f4243b6867ec0458ab7acdbc
+  branch: rails71
+  specs:
+    activerecord-virtual_attributes (7.0.0)
+      activerecord (~> 7.0)
+
+GIT
+  remote: https://github.com/kbrock/manageiq-automation_engine.git
+  revision: 6e92c8e09ea0d88e8058d759c6f0e7feafc3aa8d
+  branch: alias_attribute_access
+  specs:
+    manageiq-automation_engine (0.1.0)
+      drb
+      rubyzip (~> 2.0.0)
+
 PATH
   remote: .
   specs:
@@ -372,9 +397,6 @@ GEM
       ms_rest_azure (~> 0.12.0)
     handsoap (0.2.5.5)
       nokogiri (>= 1.2.3)
-    jquery-rjs (0.1.1.3)
-      jquery-rails
-      rails (>= 3.2)
     mime-types (3.0.0)
       mini_mime (>= 0.1.1)
 
@@ -383,59 +405,64 @@ GEM
   specs:
     PoParser (3.2.8)
       simple_po_parser (~> 1.1.6)
-    actioncable (7.0.8.7)
-      actionpack (= 7.0.8.7)
-      activesupport (= 7.0.8.7)
+    actioncable (7.1.5.1)
+      actionpack (= 7.1.5.1)
+      activesupport (= 7.1.5.1)
       nio4r (~> 2.0)
       websocket-driver (>= 0.6.1)
-    actionmailbox (7.0.8.7)
-      actionpack (= 7.0.8.7)
-      activejob (= 7.0.8.7)
-      activerecord (= 7.0.8.7)
-      activestorage (= 7.0.8.7)
-      activesupport (= 7.0.8.7)
+      zeitwerk (~> 2.6)
+    actionmailbox (7.1.5.1)
+      actionpack (= 7.1.5.1)
+      activejob (= 7.1.5.1)
+      activerecord (= 7.1.5.1)
+      activestorage (= 7.1.5.1)
+      activesupport (= 7.1.5.1)
       mail (>= 2.7.1)
       net-imap
       net-pop
       net-smtp
-    actionmailer (7.0.8.7)
-      actionpack (= 7.0.8.7)
-      actionview (= 7.0.8.7)
-      activejob (= 7.0.8.7)
-      activesupport (= 7.0.8.7)
+    actionmailer (7.1.5.1)
+      actionpack (= 7.1.5.1)
+      actionview (= 7.1.5.1)
+      activejob (= 7.1.5.1)
+      activesupport (= 7.1.5.1)
       mail (~> 2.5, >= 2.5.4)
       net-imap
       net-pop
       net-smtp
-      rails-dom-testing (~> 2.0)
-    actionpack (7.0.8.7)
-      actionview (= 7.0.8.7)
-      activesupport (= 7.0.8.7)
-      rack (~> 2.0, >= 2.2.4)
+      rails-dom-testing (~> 2.2)
+    actionpack (7.1.5.1)
+      actionview (= 7.1.5.1)
+      activesupport (= 7.1.5.1)
+      nokogiri (>= 1.8.5)
+      racc
+      rack (>= 2.2.4)
+      rack-session (>= 1.0.1)
       rack-test (>= 0.6.3)
-      rails-dom-testing (~> 2.0)
-      rails-html-sanitizer (~> 1.0, >= 1.2.0)
-    actiontext (7.0.8.7)
-      actionpack (= 7.0.8.7)
-      activerecord (= 7.0.8.7)
-      activestorage (= 7.0.8.7)
-      activesupport (= 7.0.8.7)
+      rails-dom-testing (~> 2.2)
+      rails-html-sanitizer (~> 1.6)
+    actiontext (7.1.5.1)
+      actionpack (= 7.1.5.1)
+      activerecord (= 7.1.5.1)
+      activestorage (= 7.1.5.1)
+      activesupport (= 7.1.5.1)
       globalid (>= 0.6.0)
       nokogiri (>= 1.8.5)
-    actionview (7.0.8.7)
-      activesupport (= 7.0.8.7)
+    actionview (7.1.5.1)
+      activesupport (= 7.1.5.1)
       builder (~> 3.1)
-      erubi (~> 1.4)
-      rails-dom-testing (~> 2.0)
-      rails-html-sanitizer (~> 1.1, >= 1.2.0)
-    activejob (7.0.8.7)
-      activesupport (= 7.0.8.7)
+      erubi (~> 1.11)
+      rails-dom-testing (~> 2.2)
+      rails-html-sanitizer (~> 1.6)
+    activejob (7.1.5.1)
+      activesupport (= 7.1.5.1)
       globalid (>= 0.3.6)
-    activemodel (7.0.8.7)
-      activesupport (= 7.0.8.7)
-    activerecord (7.0.8.7)
-      activemodel (= 7.0.8.7)
-      activesupport (= 7.0.8.7)
+    activemodel (7.1.5.1)
+      activesupport (= 7.1.5.1)
+    activerecord (7.1.5.1)
+      activemodel (= 7.1.5.1)
+      activesupport (= 7.1.5.1)
+      timeout (>= 0.4.0)
     activerecord-id_regions (0.5.0)
       activerecord (>= 7.0.8, < 8.0)
       activesupport (>= 7.0.8, < 8.0)
@@ -447,19 +474,24 @@ GEM
       multi_json (~> 1.11, >= 1.11.2)
       rack (>= 2.0.8, < 4)
       railties (>= 6.1)
-    activerecord-virtual_attributes (7.0.0)
-      activerecord (~> 7.0)
-    activestorage (7.0.8.7)
-      actionpack (= 7.0.8.7)
-      activejob (= 7.0.8.7)
-      activerecord (= 7.0.8.7)
-      activesupport (= 7.0.8.7)
+    activestorage (7.1.5.1)
+      actionpack (= 7.1.5.1)
+      activejob (= 7.1.5.1)
+      activerecord (= 7.1.5.1)
+      activesupport (= 7.1.5.1)
       marcel (~> 1.0)
-      mini_mime (>= 1.1.0)
-    activesupport (7.0.8.7)
+    activesupport (7.1.5.1)
+      base64
+      benchmark (>= 0.3)
+      bigdecimal
       concurrent-ruby (~> 1.0, >= 1.0.2)
+      connection_pool (>= 2.2.5)
+      drb
       i18n (>= 1.6, < 2)
+      logger (>= 1.4.2)
       minitest (>= 5.1)
+      mutex_m
+      securerandom (>= 0.3)
       tzinfo (~> 2.0)
     acts_as_tree (2.9.1)
       activerecord (>= 3.0.0)
@@ -544,6 +576,7 @@ GEM
     bcrypt (3.1.20)
     bcrypt_pbkdf (1.1.1-arm64-darwin)
     bcrypt_pbkdf (1.1.1-x86_64-darwin)
+    benchmark (0.4.0)
     bigdecimal (3.1.9)
     binary_struct (2.1.0)
     bootsnap (1.18.4)
@@ -578,8 +611,8 @@ GEM
     csv (3.3.2)
     dalli (3.2.8)
     date (3.4.1)
-    db-query-matchers (0.11.0)
-      activesupport (>= 4.0, < 7.1)
+    db-query-matchers (0.13.0)
+      activesupport (>= 4.0, < 7.3)
       rspec (>= 3.0)
     dbus-systemd (1.1.2)
       ruby-dbus (~> 0.14)
@@ -849,6 +882,7 @@ GEM
       ffi-compiler (~> 1.0)
       rake (~> 13.0)
     locale (2.1.4)
+    logger (1.6.5)
     loofah (2.24.0)
       crass (~> 1.0.2)
       nokogiri (>= 1.12.0)
@@ -859,8 +893,8 @@ GEM
       net-imap
       net-pop
       net-smtp
-    manageiq-api-client (0.5.0)
-      activesupport (>= 6.0, < 7.1)
+    manageiq-api-client (0.6.0)
+      activesupport (>= 7.0, < 8.0)
       faraday (>= 1.0, < 3.0)
       faraday-follow_redirects
       json (~> 2.3)
@@ -1000,9 +1034,6 @@ GEM
     ostruct (0.6.1)
     ovirt-engine-sdk (4.6.0)
       json (>= 1, < 3)
-    ovirt_metrics (3.2.0)
-      activerecord (>= 6.0)
-      pg
     parallel (1.26.3)
     parallel_tests (4.9.0)
       parallel
@@ -1048,22 +1079,27 @@ GEM
     rack (2.2.10)
     rack-attack (6.5.0)
       rack (>= 1.0, < 3)
+    rack-session (1.0.2)
+      rack (< 3)
     rack-test (2.2.0)
       rack (>= 1.3)
-    rails (7.0.8.7)
-      actioncable (= 7.0.8.7)
-      actionmailbox (= 7.0.8.7)
-      actionmailer (= 7.0.8.7)
-      actionpack (= 7.0.8.7)
-      actiontext (= 7.0.8.7)
-      actionview (= 7.0.8.7)
-      activejob (= 7.0.8.7)
-      activemodel (= 7.0.8.7)
-      activerecord (= 7.0.8.7)
-      activestorage (= 7.0.8.7)
-      activesupport (= 7.0.8.7)
+    rackup (1.0.1)
+      rack (< 3)
+      webrick
+    rails (7.1.5.1)
+      actioncable (= 7.1.5.1)
+      actionmailbox (= 7.1.5.1)
+      actionmailer (= 7.1.5.1)
+      actionpack (= 7.1.5.1)
+      actiontext (= 7.1.5.1)
+      actionview (= 7.1.5.1)
+      activejob (= 7.1.5.1)
+      activemodel (= 7.1.5.1)
+      activerecord (= 7.1.5.1)
+      activestorage (= 7.1.5.1)
+      activesupport (= 7.1.5.1)
       bundler (>= 1.15.0)
-      railties (= 7.0.8.7)
+      railties (= 7.1.5.1)
     rails-dom-testing (2.2.0)
       activesupport (>= 5.0.0)
       minitest
@@ -1074,13 +1110,14 @@ GEM
     rails-i18n (7.0.10)
       i18n (>= 0.7, < 2)
       railties (>= 6.0.0, < 8)
-    railties (7.0.8.7)
-      actionpack (= 7.0.8.7)
-      activesupport (= 7.0.8.7)
-      method_source
+    railties (7.1.5.1)
+      actionpack (= 7.1.5.1)
+      activesupport (= 7.1.5.1)
+      irb
+      rackup (>= 1.0.0)
       rake (>= 12.2)
-      thor (~> 1.0)
-      zeitwerk (~> 2.5)
+      thor (~> 1.0, >= 1.2.2)
+      zeitwerk (~> 2.6)
     rainbow (3.1.1)
     rake (13.2.1)
     rb-fsevent (0.11.2)
@@ -1190,6 +1227,7 @@ GEM
     sd_notify (0.1.1)
     secure_headers (3.9.0)
       useragent
+    securerandom (0.4.1)
     server_sent_events (0.1.3)
     sexp_processor (4.17.3)
     signet (0.19.0)
@@ -1302,6 +1340,7 @@ GEM
       activesupport (>= 4.2)
       multi_json (~> 1.2)
       railties (>= 4.2)
+    webrick (1.9.1)
     websocket-driver (0.6.5)
       websocket-extensions (>= 0.1.0)
     websocket-extensions (0.1.5)
@@ -1329,7 +1368,7 @@ PLATFORMS
 DEPENDENCIES
   PoParser
   activerecord-session_store (~> 2.0)
-  activerecord-virtual_attributes (~> 7.0.0)
+  activerecord-virtual_attributes!
   acts_as_tree (~> 2.7)
   amazon_ssa_support!
   american_date
@@ -1349,11 +1388,10 @@ DEPENDENCIES
   capybara (~> 2.5.0)
   cgi (~> 0.3.5)
   color (~> 1.8)
-  concurrent-ruby (< 1.3.5)
   config (~> 5.1)
   connection_pool
   dalli (~> 3.2.3)
-  db-query-matchers (~> 0.11.0)
+  db-query-matchers (~> 0.13.0)
   dbus-systemd (~> 1.1.0)
   default_value_for (~> 4.0)
   docker-api (~> 1.33.6)
@@ -1369,24 +1407,24 @@ DEPENDENCIES
   hamlit (~> 2.11.0)
   handsoap (= 0.2.5.5)!
   inifile (~> 3.0)
-  inventory_refresh (~> 2.1)
+  inventory_refresh (~> 2.2)
   irb (= 1.4.1)
-  jquery-rjs (= 0.1.1.3)!
+  jquery-rjs!
   kubeclient (~> 4.0)
   linux_admin (>= 3.0, < 5)
   listen (~> 3.2)
   manageiq-api!
-  manageiq-api-client (~> 0.5.0)
+  manageiq-api-client (~> 0.6.0)
   manageiq-appliance_console (~> 10.0)
   manageiq-automation_engine!
   manageiq-consumption!
   manageiq-content!
   manageiq-decorators!
   manageiq-gems-pending (> 0)!
-  manageiq-loggers (~> 1.0, >= 1.1.1)
-  manageiq-messaging (~> 1.0, >= 1.4.3)
+  manageiq-loggers (~> 1.2)
+  manageiq-messaging (~> 1.5)
   manageiq-password (~> 1.0)
-  manageiq-postgres_ha_admin (~> 3.3)
+  manageiq-postgres_ha_admin (~> 3.4)
   manageiq-providers-amazon!
   manageiq-providers-ansible_tower!
   manageiq-providers-autosde!
@@ -1429,6 +1467,7 @@ DEPENDENCIES
   net-ping (~> 1.7.4)
   openscap (~> 0.4.8)
   optimist (~> 3.0)
+  ovirt_metrics!
   parallel_tests (~> 4.4)
   pg (>= 1.4.1)
   pg-dsn_parser (~> 0.1.1)
@@ -1441,7 +1480,7 @@ DEPENDENCIES
   query_relation (~> 0.1.0)
   rack (>= 2.2.6.4)
   rack-attack (~> 6.5.0)
-  rails (~> 7.0.8, >= 7.0.8.7)
+  rails (~> 7.1.5, >= 7.1.5.1)
   rails-i18n (~> 7.x)
   rake (>= 12.3.3)
   rdoc

@Fryguy
Copy link
Member

Fryguy commented Jan 31, 2025

Side q - I'm curious why some gems downgraded like the ibm_vpc ones

@jrafanie
Copy link
Member Author

Side q - I'm curious why some gems downgraded like the ibm_vpc ones

let me rebase my rails71 branch and see if it cleans up some of those changes.

"has_default_path" => false,
"short_description" => "EmsRefreshSpecProductWithNoPortfolio desc",
"support_description" => nil
Copy link
Member Author

@jrafanie jrafanie Jan 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, if the cassette has these values, the test passes fine with rails 7.1, see line 1470 below...

            "support_url"         => "http://EmsRefreshSpec.com",
            "support_email"       => "[email protected]",
            "has_default_path"    => false,
            "short_description"   => "EmsRefreshSpecProduct description",
            "support_description" => "EmsRefreshSpec desc"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words, if the cassette has this information, it's populated, if the keys don't exist in the cassette with rails 7.1, the key doesn't even exist.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agrare thoughts on this removal? I don't know why these values are populated as nil with rails 7.0 if missing from the cassette and not specified at all with rails 7.1. 🤷

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there is a difference in the record being saved or if the difference is in the matcher picking up on the nested hash value? Let me try running this on rails 7.0 and dump that property for each iteration and see what we get.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we don't set those keys specifically we just take the whole product_view_summary from AWS

        :extra   => {
          :product_view_summary => service_offering.product_view_summary,

I did throw in a expect(@service_offering_with_no_portfolio.extra["product_view_summary"]).to have_key("support_description") to check that the key always existed and wasn't just missing and that passed on 7.0.

I'll try pulling down your 7.1 branch and see if I can spot the difference (I wonder if setting a struct Aws::ServiceCatalog::Types::ProductViewSummary to a jsonb field is being handled differently in 7.1?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try the following on that struct in each version? This is what's used via the ActiveRecord::Type::Json class. Ultimately, I it calls as_json under the covers.

pp value
e = ActiveSupport::JSON.encode(value)
pp e
d = ActiveSupport::JSON.decode(e)
pp d

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I just tried it myself, and it's definitely the as_json method changed:

Taking our app out of the equation, I wrote this script

require "bundler/inline"

gemfile do
  gem "aws-sdk-servicecatalog"
  gem "activesupport", ARGV[0], :require => "active_support/all"
end

puts "Using ActiveSupport #{ActiveSupport.version}"
puts

pvs = Aws::ServiceCatalog::Types::ProductViewSummary.new(
  :id                  => "prodview-mojlvmp5xax74",
  :product_id          => "prod-4v6rc4hwaiiha",
  :name                => "EmsRefreshSpecProductWithNoPortfolio",
  :owner               => "EmsRefreshSpecProductWithNoPortfolioOwner",
  :short_description   => "EmsRefreshSpecProductWithNoPortfolio desc",
  :type                => "CLOUD_FORMATION_TEMPLATE",
  :distributor         => "",
  :has_default_path    => false,
  :support_email       => nil,
  :support_description => nil,
  :support_url         => nil
)
pp pvs.as_json

Here's the results on different rails versions:

$ ruby struct_as_json.rb "~>7.0.8"
Using ActiveSupport 7.0.8.7

{"id"=>"prodview-mojlvmp5xax74",
 "product_id"=>"prod-4v6rc4hwaiiha",
 "name"=>"EmsRefreshSpecProductWithNoPortfolio",
 "owner"=>"EmsRefreshSpecProductWithNoPortfolioOwner",
 "short_description"=>"EmsRefreshSpecProductWithNoPortfolio desc",
 "type"=>"CLOUD_FORMATION_TEMPLATE",
 "distributor"=>"",
 "has_default_path"=>false,
 "support_email"=>nil,
 "support_description"=>nil,
 "support_url"=>nil}

$ ruby struct_as_json.rb "~>7.1.5"
Using ActiveSupport 7.1.5.1

{"id"=>"prodview-mojlvmp5xax74",
 "product_id"=>"prod-4v6rc4hwaiiha",
 "name"=>"EmsRefreshSpecProductWithNoPortfolio",
 "owner"=>"EmsRefreshSpecProductWithNoPortfolioOwner",
 "short_description"=>"EmsRefreshSpecProductWithNoPortfolio desc",
 "type"=>"CLOUD_FORMATION_TEMPLATE",
 "distributor"=>"",
 "has_default_path"=>false}

$ ruby struct_as_json.rb "~>7.2.2"
Using ActiveSupport 7.2.2.1

{"id"=>"prodview-mojlvmp5xax74",
 "product_id"=>"prod-4v6rc4hwaiiha",
 "name"=>"EmsRefreshSpecProductWithNoPortfolio",
 "owner"=>"EmsRefreshSpecProductWithNoPortfolioOwner",
 "short_description"=>"EmsRefreshSpecProductWithNoPortfolio desc",
 "type"=>"CLOUD_FORMATION_TEMPLATE",
 "distributor"=>"",
 "has_default_path"=>false}

$ ruby struct_as_json.rb "~>8.0.1"
Using ActiveSupport 8.0.1

{"id"=>"prodview-mojlvmp5xax74",
 "product_id"=>"prod-4v6rc4hwaiiha",
 "name"=>"EmsRefreshSpecProductWithNoPortfolio",
 "owner"=>"EmsRefreshSpecProductWithNoPortfolioOwner",
 "short_description"=>"EmsRefreshSpecProductWithNoPortfolio desc",
 "type"=>"CLOUD_FORMATION_TEMPLATE",
 "distributor"=>"",
 "has_default_path"=>false}

Copy link
Member

@Fryguy Fryguy Feb 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agrare @jrafanie I narrowed it down to 7.1.0.beta1, and so I'm pretty sure it's this change that landed in Rails 7.1.0.beta1: rails/rails@a7d4d78

It's weird, but that leads to rails/rails#47273, and if you follow that thread you will see fixes to the Data object for empty fields. I think this is a bug in Rails that Structs deserialize without their nil fields.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh! even better. So, with that change above, as_json calls .to_h, But in aws-sdk-core their struct#to_h method removes nils by default

https://github.com/aws/aws-sdk-ruby/blob/version-3/gems/aws-sdk-core/lib/aws-sdk-core/structure.rb#L24-L25

Copy link
Member

@Fryguy Fryguy Feb 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smaller reproducer:

require "bundler/inline"

gemfile do
  gem "aws-sdk-core"
  gem "activesupport", ARGV[0]
end

require "logger" # LOL
require "active_support"
require "active_support/core_ext/object/json"
puts "Using ActiveSupport #{ActiveSupport.version}"

MyStruct = Struct.new(:not_missing, :missing) do
  include Aws::Structure # This is what aws-sdk gems do with their structures, and it brings in an alternative to_h
end
s = MyStruct.new(:not_missing => "foo", :missing => nil)
puts "as_json: #{s.as_json}"
puts "to_h:    #{s.to_h}"
$ ruby struct_as_json.rb "=7.0.8.7"
Using ActiveSupport 7.0.8.7
as_json: {"not_missing"=>"foo", "missing"=>nil}
to_h:    {:not_missing=>"foo"}

$ ruby struct_as_json.rb "=7.1.0.beta1"
Using ActiveSupport 7.1.0.beta1
as_json: {"not_missing"=>"foo"}
to_h:    {:not_missing=>"foo"}

@jrafanie
Copy link
Member Author

Side q - I'm curious why some gems downgraded like the ibm_vpc ones

let me rebase my rails71 branch and see if it cleans up some of those change

Ok, rebased and updated the Gemfile.lock differences. I'll look at other errors and come back to this later. I'm not really sure what's happening here.

@jrafanie
Copy link
Member Author

In chat, I mentioned it will could be related to this: ManageIQ/manageiq#23275

jrafanie and others added 2 commits February 14, 2025 10:03
support_url, support_email, and support_description don't exist in the response payload
in the cassette for these tests and yet previously, these fields were set as nil with rails
7.0.  It's unclear what changed with rails 7.1 but now these nil value fields aren't persisted
in the extra column.

collector/cloud_manager.rb:

  def service_offerings
    aws_service_catalog.client.search_products_as_admin.product_view_details
    ...
  end

parser/cloud_manager.rb:

  collector.service_offerings.each do |service_offering|
    persister_service_offering = persister.service_offerings.build(...
      ...
      :extra   => {
        :product_view_summary => service_offering.product_view_summary,
    ...
Prevent differing behavior between rails versions when assigning a
Struct to a jsonb object by explicitly converting to a hash first.
@agrare
Copy link
Member

agrare commented Feb 14, 2025

I tried reverting the change to the newer config gem to continue to use deep_merge from the gem and not active_support and i saw the same behavior on rails 7.1.

I attempted to bisect v7.0 .. v7.1 but there were too many issues that were fixed in newer versions that prevented me from testing the merge base between the two.

The issue is definitely how the struct is persisted to a jsonb column, in order to behave consistently between 7.0 and 7.1 I updated the parser to call to_h on the struct before assigning it to the InventoryObject to be persisted.

I have a commit in this branch now so assigning to @Fryguy

@agrare agrare assigned Fryguy and unassigned agrare Feb 14, 2025
@agrare agrare changed the title [WIP] With rails 7.1, these nil values are not persisted With rails 7.1, these nil values are not persisted Feb 14, 2025
@agrare agrare removed the wip label Feb 14, 2025
@Fryguy
Copy link
Member

Fryguy commented Feb 14, 2025

Merging this PR as it works on both Rails 7.0 and 7.1.

tl;dr Rails jsonb column encoding uses as_json; as_json was changed in activesupport 7.1.0.beta1 to call to_h; aws-sdk structs have a custom to_h that removes nil values.

We might need to evaluate separately if other structs should be to_h, or perhaps to_orig_h depending on if we actually want the nil values.

@Fryguy Fryguy merged commit eedfa55 into ManageIQ:master Feb 14, 2025
4 checks passed
@@ -404,7 +404,7 @@ def service_offerings
:name => service_offering.product_view_summary.name,
:ems_ref => service_offering.product_view_summary.product_id,
:extra => {
:product_view_summary => service_offering.product_view_summary,
:product_view_summary => service_offering.product_view_summary.to_h,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all that for this... 🤣

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants