Skip to content

Commit 2deb71c

Browse files
committed
Prevent serializing huge graphs of relationships between users and companies
This prevents humongous payloads for situations where users are part of many companies which again have many users. The previous implementation would continue serializing until the entire graph has been serialized.
1 parent 63f74dc commit 2deb71c

File tree

3 files changed

+33
-52
lines changed

3 files changed

+33
-52
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased (main)
44

5+
- Prevents serializing of large graphs of relationships between users and companies
6+
57
## v1.1.0 (2025-10-17)
68

79
- Allow specifying companies in messages

lib/userlist/push/serializer.rb

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,23 @@ def serialize?(resource)
2828
private
2929

3030
def serialize_resource(resource)
31-
return resource.identifier if serialized_resources.include?(resource)
31+
preventing_circular_serialization(resource, fallback: resource.identifier) do
32+
return unless serialize?(resource)
3233

33-
serialized_resources << resource
34+
serialized = {}
3435

35-
return unless serialize?(resource)
36+
resource.attribute_names.each do |name|
37+
serialized[name] = resource.send(name)
38+
end
3639

37-
serialized = {}
40+
resource.association_names.each do |name|
41+
next unless result = serialize_association(resource.send(name))
3842

39-
resource.attribute_names.each do |name|
40-
serialized[name] = resource.send(name)
41-
end
42-
43-
resource.association_names.each do |name|
44-
next unless result = serialize_association(resource.send(name))
43+
serialized[name] = result
44+
end
4545

46-
serialized[name] = result
46+
serialized
4747
end
48-
49-
serialized
5048
end
5149

5250
def serialize_association(association)
@@ -74,6 +72,23 @@ def serialize_collection(collection)
7472
def serialized_resources
7573
@serialized_resources ||= Set.new
7674
end
75+
76+
def serialized_types
77+
@serialized_types ||= Set.new
78+
end
79+
80+
def preventing_circular_serialization(resource, fallback: nil)
81+
return fallback if serialized_types.include?(resource.class) || serialized_resources.include?(resource)
82+
83+
serialized_types.add(resource.class)
84+
serialized_resources.add(resource)
85+
86+
begin
87+
yield
88+
ensure
89+
serialized_types.delete(resource.class)
90+
end
91+
end
7792
end
7893
end
7994
end

spec/userlist/push/serializer_spec.rb

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -170,19 +170,7 @@
170170
company: {
171171
identifier: 'company-identifier',
172172
name: 'Example, Inc.',
173-
signed_up_at: nil,
174-
relationships: [
175-
{
176-
company: 'company-identifier',
177-
user: {
178-
identifier: 'other-user-identifier',
179-
email: 'bar@example.com'
180-
},
181-
properties: {
182-
role: 'member'
183-
}
184-
}
185-
]
173+
signed_up_at: nil
186174
},
187175
properties: {
188176
role: 'admin'
@@ -384,19 +372,7 @@
384372
company: {
385373
identifier: 'company-identifier',
386374
name: 'Example, Inc.',
387-
signed_up_at: nil,
388-
relationships: [
389-
{
390-
company: 'company-identifier',
391-
user: {
392-
identifier: 'other-user-identifier',
393-
email: 'bar@example.com'
394-
},
395-
properties: {
396-
role: 'member'
397-
}
398-
}
399-
]
375+
signed_up_at: nil
400376
},
401377
properties: {
402378
role: 'admin'
@@ -509,19 +485,7 @@
509485
company: {
510486
identifier: 'company-identifier',
511487
name: 'Example, Inc.',
512-
signed_up_at: nil,
513-
relationships: [
514-
{
515-
company: 'company-identifier',
516-
user: {
517-
identifier: 'other-user-identifier',
518-
email: 'bar@example.com'
519-
},
520-
properties: {
521-
role: 'member'
522-
}
523-
}
524-
]
488+
signed_up_at: nil
525489
},
526490
properties: {
527491
role: 'admin'

0 commit comments

Comments
 (0)