Skip to content

Commit f952c24

Browse files
KirchoKirevrichmolj
authored andcommitted
Fix many_to_many validation exception when sideposting
When adding many_to_many related associations - for example department.teams << new_team, `save!` gets called on the related new_team and it throws a ValidationError that breaks the normal graphiti error handling. Prevent this by checking if the association is valid (has blank errors) before adding it.
1 parent 96e0f20 commit f952c24

File tree

2 files changed

+57
-3
lines changed

2 files changed

+57
-3
lines changed

lib/graphiti/adapters/active_record.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,8 @@ def associate_all(parent, children, association_name, association_type)
240240
children.each do |child|
241241
if association_type == :many_to_many &&
242242
[:create, :update].include?(Graphiti.context[:namespace]) &&
243-
!parent.send(association_name).exists?(child.id)
243+
!parent.send(association_name).exists?(child.id) &&
244+
child.errors.blank?
244245
parent.send(association_name) << child
245246
else
246247
target = association.instance_variable_get(:@target)

spec/integration/rails/persistence_spec.rb

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,11 @@ def self.name
750750
{type: "positions", 'temp-id': "pos1", method: "create"},
751751
{type: "positions", 'temp-id': "pos2", method: "create"}
752752
]
753+
},
754+
teams: {
755+
data: [
756+
{type: "teams", 'temp-id': "team1", method: "create"}
757+
]
753758
}
754759
}
755760
},
@@ -773,6 +778,11 @@ def self.name
773778
type: "positions",
774779
'temp-id': "pos2",
775780
attributes: {title: "manager"}
781+
},
782+
{
783+
type: "teams",
784+
'temp-id': 'team1',
785+
attributes: {name: "Team 1"}
776786
}
777787
]
778788
}
@@ -785,12 +795,14 @@ def self.name
785795
employee = Employee.first
786796
positions = employee.positions
787797
department = employee.positions[0].department
798+
team = employee.teams[0]
788799

789800
expect(employee.first_name).to eq("Joe")
790801
expect(positions.length).to eq(2)
791802
expect(positions[0].title).to eq("specialist")
792803
expect(positions[1].title).to eq("manager")
793804
expect(department.name).to eq("safety")
805+
expect(team.name).to eq("Team 1")
794806
end
795807

796808
context "when a has_many relationship has validation error" do
@@ -810,7 +822,7 @@ def self.name
810822
it "rolls back the entire transaction" do
811823
expect {
812824
make_request
813-
}.to_not(change { Employee.count + Position.count + Department.count })
825+
}.to_not(change { Employee.count + Position.count + Department.count + Team.count })
814826
error = json["errors"].find do |err|
815827
err.fetch("meta", {}).fetch("relationship", {}).fetch("type", nil) == "positions"
816828
end
@@ -851,7 +863,7 @@ def self.name
851863
it "rolls back the entire transaction" do
852864
expect {
853865
make_request
854-
}.to_not(change { Employee.count + Position.count + Department.count })
866+
}.to_not(change { Employee.count + Position.count + Department.count + Team.count })
855867
error = json["errors"].find do |err|
856868
err.fetch("meta", {}).fetch("relationship", {}).fetch("type", nil) == "departments"
857869
end
@@ -875,6 +887,47 @@ def self.name
875887
end
876888
end
877889

890+
context "when a many_to_many relationship has a validation error" do
891+
around do |e|
892+
begin
893+
Team.validates :name, presence: true
894+
e.run
895+
ensure
896+
Team.clear_validators!
897+
end
898+
end
899+
900+
before do
901+
payload[:included][3][:attributes].delete(:name)
902+
end
903+
904+
it "rolls back the entire transaction" do
905+
expect {
906+
make_request
907+
}.to_not(change { Employee.count + Position.count + Department.count + Team.count })
908+
error = json["errors"].find do |err|
909+
err.fetch("meta", {}).fetch("relationship", {}).fetch("type", nil) == "teams"
910+
end
911+
912+
expect(error).to match(
913+
"code" => "unprocessable_entity",
914+
"detail" => "Name can't be blank",
915+
"meta" => {
916+
"relationship" => hash_including(
917+
"attribute" => "name",
918+
"message" => "can't be blank",
919+
"name" => "teams",
920+
"temp-id" => "team1",
921+
"type" => "teams"
922+
)
923+
},
924+
"source" => {"pointer" => "/data/attributes/name"},
925+
"status" => "422",
926+
"title" => "Validation Error"
927+
)
928+
end
929+
end
930+
878931
context "when associating to an existing record" do
879932
let!(:classification) { Classification.create!(description: "senior") }
880933

0 commit comments

Comments
 (0)