Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ gem "standardrb"
gem "web-console", group: :development

# Databases to test against
gem "pg"
gem "mysql2"
gem "pg"
gem "sqlite3"

gem "propshaft"
gem "turbo-rails"
gem "stimulus-rails"
gem "turbo-rails"
34 changes: 25 additions & 9 deletions app/controllers/madmin/resource_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Madmin
class ResourceController < Madmin::ApplicationController
include SortHelper

before_action :set_record, except: [:index, :new, :create]
before_action :set_record, except: %i[index new create]

# Assign current_user for paper_trail gem
before_action :set_paper_trail_whodunnit, if: -> { respond_to?(:set_paper_trail_whodunnit, true) }
Expand All @@ -12,9 +12,9 @@ def index

respond_to do |format|
format.html
format.json {
format.json do
render json: @records.map { |r| {name: @resource.display_name(r), id: r.id} }
}
end
end
end

Expand Down Expand Up @@ -77,9 +77,16 @@ def valid_scope
end

def resource_params
params.require(resource.param_key)
permitted_params = params.require(resource.param_key)
.permit(*resource.permitted_params)
.transform_values { |v| change_polymorphic(v) }

# Transform JSON fields
permitted_params.each do |key, value|
permitted_params[key] = parse_json_attribute(key, value)
end

permitted_params
end

def new_resource_params
Expand All @@ -91,11 +98,20 @@ def new_resource_params
def change_polymorphic(data)
return data unless data.is_a?(ActionController::Parameters) && data[:type]

if data[:type] == "polymorphic"
GlobalID::Locator.locate(data[:value])
else
raise "Unrecognised param data: #{data.inspect}"
end
raise "Unrecognised param data: #{data.inspect}" unless data[:type] == "polymorphic"

GlobalID::Locator.locate(data[:value])
end

def parse_json_attribute(key, value)
return value unless json_attribute?(key)

JSON.parse(value)
end

def json_attribute?(key)
attribute = resource.get_attribute(key.to_sym)
attribute&.type == :json
end

def search_term
Expand Down
60 changes: 60 additions & 0 deletions test/integration/json_fields_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require "test_helper"

class JsonFieldsTest < ActionDispatch::IntegrationTest
test "can update model with JSON field" do
post = posts(:one)
json_metadata = '{"tags": ["ruby", "rails"], "priority": "high"}'

put madmin_post_path(post), params: {
post: {
title: "Updated Post",
metadata: json_metadata
}
}

assert_response :redirect

post.reload
assert_equal "Updated Post", post.title
assert_equal({"tags" => %w[ruby rails], "priority" => "high"}, post.metadata)
assert_instance_of Hash, post.metadata
end

test "handles invalid JSON gracefully in update" do
post = posts(:one)
invalid_json = '{"tags": ["ruby", "rails", "priority": "high"}' # Missing closing bracket

put madmin_post_path(post), params: {
post: {
title: "Updated Post",
metadata: invalid_json
}
}

assert_response :redirect

post.reload
assert_equal "Updated Post", post.title
# Should keep the invalid JSON as a string since parsing failed
assert_equal invalid_json, post.metadata
end

test "does not affect non-JSON fields during update" do
post = posts(:one)

put madmin_post_path(post), params: {
post: {
title: "Simple Title",
metadata: '{"test": true}'
}
}

assert_response :redirect

post.reload
assert_equal "Simple Title", post.title
assert_instance_of String, post.title
assert_equal({"test" => true}, post.metadata)
assert_instance_of Hash, post.metadata
end
end