Skip to content

Commit 5ff4dd2

Browse files
committed
refactor(events): pass event contexts
This change unblocks the initial BillingSegment refactor needed to support BillingPeriodFilterService. BillingSegment filtering queries raw events through contract data, while the existing event stores and billable metric aggregators assumed every event context was a Subscription. Introduce Events::Stores::EventContext as the explicit object passed into event stores and aggregators. The stores now ask the context for external ids, organization, customer, duration, subscription history, and date calculations instead of depending directly on a subscription. Contract-backed contexts currently support the raw event lookup surface needed by BillingSegment, and intentionally raise NotImplementedError for subscription-specific methods that are not defined yet: invoice_subscriptions, previous_subscription, previous_subscription_id, previous_subscription_id?, next_subscription, upgraded?, downgraded?, id, and charges_duration_at. Those contract behaviours will be addressed in a separate PR.
1 parent 8b0a3b0 commit 5ff4dd2

39 files changed

Lines changed: 396 additions & 112 deletions

app/services/billable_metrics/aggregation_factory.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
module BillableMetrics
44
class AggregationFactory
5-
def self.new_instance(charge:, current_usage: false, **attributes)
5+
def self.new_instance(charge:, context:, current_usage: false, **attributes)
66
aggregator_class(charge, current_usage).new(
77
event_store_class: Events::Stores::StoreFactory.store_class(organization: charge.billable_metric.organization),
88
charge:,
9+
context:,
910
**attributes
1011
)
1112
end

app/services/billable_metrics/aggregations/base_service.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ def self.null_result(result, grouped_by_keys: nil, apply_aggregation: false)
4848
result
4949
end
5050

51-
def initialize(event_store_class:, charge:, subscription:, boundaries:, filters: {}, bypass_aggregation: false)
51+
def initialize(event_store_class:, charge:, context:, boundaries:, filters: {}, bypass_aggregation: false)
5252
super(nil)
5353
@event_store_class = event_store_class
5454
@charge = charge
55-
@subscription = subscription
55+
@context = context
5656

5757
@filters = filters
5858
@charge_filter = filters[:charge_filter]
@@ -133,7 +133,7 @@ def empty_results
133133

134134
attr_accessor :event_store_class,
135135
:charge,
136-
:subscription,
136+
:context,
137137
:filters,
138138
:charge_filter,
139139
:event,
@@ -146,12 +146,12 @@ def empty_results
146146

147147
delegate :billable_metric, to: :charge
148148

149-
delegate :customer, to: :subscription
149+
delegate :customer, to: :context
150150

151151
def event_store
152152
@event_store ||= event_store_class.new(
153153
code: billable_metric.code,
154-
subscription:,
154+
context:,
155155
boundaries:,
156156
filters:,
157157
deduplicate: deduplicate?
@@ -162,7 +162,7 @@ def deduplicate?
162162
override = Events::Stores::StoreFactory.override
163163
return override[:deduplicate] if override
164164

165-
organization = subscription&.organization
165+
organization = context&.organization
166166
return false unless organization
167167

168168
organization.clickhouse_events_store? && organization.clickhouse_deduplication_enabled?
@@ -230,7 +230,7 @@ def empty_result
230230
def find_cached_aggregation(with_from_datetime:, with_to_datetime:, grouped_by: nil)
231231
query = CachedAggregation
232232
.where(organization_id: billable_metric.organization_id)
233-
.where(external_subscription_id: subscription.external_id)
233+
.where(external_subscription_id: context.external_id)
234234
.where(charge_id: charge.id)
235235
.from_datetime(with_from_datetime)
236236
.to_datetime(with_to_datetime)

app/services/billable_metrics/aggregations/custom_service.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def latest_state(grouped_by_values:)
9696

9797
query = CachedAggregation
9898
.where(organization_id: billable_metric.organization_id)
99-
.where(external_subscription_id: subscription.external_id)
99+
.where(external_subscription_id: context.external_id)
100100
.where(charge_id: charge.id)
101101
.where("cached_aggregations.timestamp < ?", truncated_datetime)
102102
.where(grouped_by: grouped_by_values.presence || {})
@@ -128,7 +128,7 @@ def perform_custom_aggregation(target_result: result, grouped_by_values: nil)
128128
if grouped_by_values
129129
store = event_store_class.new(
130130
code: billable_metric.code,
131-
subscription:,
131+
context:,
132132
boundaries:,
133133
filters: filters.merge(grouped_by_values:)
134134
)

app/services/billable_metrics/aggregations/weighted_sum_service.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def latest_value
101101
return @latest_value = latest_cached_aggregation.current_aggregation
102102
end
103103

104-
if subscription.previous_subscription_id?
104+
if context.previous_subscription_id?
105105
return @latest_value = latest_value_from_events.first
106106
end
107107

@@ -115,7 +115,7 @@ def latest_breakdowns
115115
return @latest_breakdowns = latest_cached_aggregation.presentation_breakdowns
116116
end
117117

118-
if subscription.previous_subscription_id?
118+
if context.previous_subscription_id?
119119
return @latest_breakdowns = latest_value_from_events.second
120120
end
121121

@@ -135,7 +135,7 @@ def latest_cached_aggregations
135135

136136
query = CachedAggregation
137137
.where(organization_id: billable_metric.organization_id)
138-
.where(external_subscription_id: subscription.external_id)
138+
.where(external_subscription_id: context.external_id)
139139
.where(charge_id: charge.id)
140140
.where(timestamp: ...from_datetime)
141141
.order(timestamp: :desc, created_at: :desc)
@@ -152,7 +152,7 @@ def latest_value_from_events
152152

153153
event_store = event_store_class.new(
154154
code: billable_metric.code,
155-
subscription:,
155+
context:,
156156
boundaries: {to_datetime: from_datetime - 1.second},
157157
filters:
158158
)
@@ -178,7 +178,7 @@ def grouped_latest_values
178178
end
179179
end
180180

181-
if subscription.previous_subscription_id?
181+
if context.previous_subscription_id?
182182
return @grouped_latest_values = grouped_latest_values_from_events.first
183183
end
184184

@@ -192,7 +192,7 @@ def grouped_latest_breakdowns
192192
return @grouped_latest_breakdowns = grouped_latest_cached_aggregations.flat_map(&:presentation_breakdowns)
193193
end
194194

195-
if subscription.previous_subscription_id?
195+
if context.previous_subscription_id?
196196
return @grouped_latest_breakdowns = grouped_latest_values_from_events.second
197197
end
198198

@@ -214,7 +214,7 @@ def grouped_latest_cached_aggregations
214214
def grouped_latest_values_from_events
215215
event_store = event_store_class.new(
216216
code: billable_metric.code,
217-
subscription:,
217+
context:,
218218
boundaries: {to_datetime: from_datetime - 1.second},
219219
filters:
220220
)

app/services/billable_metrics/breakdown/sum_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def to_date_in_customer_timezone
2727
def persisted_breakdown
2828
event_store = event_store_class.new(
2929
code: billable_metric.code,
30-
subscription:,
30+
context:,
3131
boundaries: {to_datetime: from_datetime},
3232
filters:
3333
)

app/services/billable_metrics/prorated_aggregations/base_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def period_duration
101101
# we want to bill the persisted metrics at prorata of the full period duration.
102102
# ie: the number of day of the terminated period divided by number of days without termination
103103
def persisted_pro_rata
104-
subscription.date_diff_with_timezone(from_datetime, to_datetime).fdiv(period_duration)
104+
context.date_diff_with_timezone(from_datetime, to_datetime).fdiv(period_duration)
105105
end
106106

107107
attr_accessor :options

app/services/billable_metrics/prorated_aggregations/sum_service.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def persisted_event_store_instance
148148
@persisted_event_store_instance ||= begin
149149
event_store = event_store_class.new(
150150
code: billable_metric.code,
151-
subscription:,
151+
context:,
152152
boundaries: {to_datetime: from_datetime - PERSISTED_TOP_BOUNDARY_DELAY}, # Note: Avoid counting events exactly on `from_datetime` twice
153153
filters:,
154154
deduplicate: deduplicate?
@@ -175,7 +175,7 @@ def current_prorated_result
175175
def persisted_prorated_result
176176
@persisted_prorated_result ||= persisted_event_store_instance.prorated_sum(
177177
period_duration:,
178-
persisted_duration: subscription.date_diff_with_timezone(from_datetime, to_datetime)
178+
persisted_duration: context.date_diff_with_timezone(from_datetime, to_datetime)
179179
)
180180
end
181181

@@ -242,7 +242,7 @@ def current_grouped_prorated_results
242242
def persisted_grouped_prorated_results
243243
@persisted_grouped_prorated_results ||= persisted_event_store_instance.grouped_prorated_sum(
244244
period_duration:,
245-
persisted_duration: subscription.date_diff_with_timezone(from_datetime, to_datetime)
245+
persisted_duration: context.date_diff_with_timezone(from_datetime, to_datetime)
246246
)
247247
end
248248
end

app/services/charges/pay_in_advance_aggregation_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def initialize(charge:, boundaries:, properties:, event:, charge_filter: nil)
1717
def call
1818
aggregator = BillableMetrics::AggregationFactory.new_instance(
1919
charge:,
20-
subscription:,
20+
context: Events::Stores::EventContext.from(subscription:),
2121
boundaries: {
2222
from_datetime: boundaries.charges_from_datetime,
2323
to_datetime: boundaries.charges_to_datetime,

app/services/events/billing_period_filters/charges_resolver.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def period_start
3131
def event_store
3232
@event_store ||= Events::Stores::StoreFactory.new_instance(
3333
organization: organization,
34-
subscription:,
34+
context: Events::Stores::EventContext.from(subscription:),
3535
boundaries: {
3636
from_datetime: boundaries.charges_from_datetime,
3737
to_datetime: boundaries.charges_to_datetime

app/services/events/stores/base_store.rb

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ def to_grouped_hash
5656
:events_count
5757
)
5858

59-
def initialize(subscription:, boundaries:, code: nil, filters: {}, deduplicate: false)
59+
def initialize(context:, boundaries:, code: nil, filters: {}, deduplicate: false)
6060
@code = code
61-
@subscription = subscription
61+
@context = context
6262
@boundaries = boundaries
6363

6464
@filters = filters
@@ -183,7 +183,7 @@ def to_datetime
183183
end
184184

185185
def charges_duration
186-
boundaries[:charges_duration]
186+
period_duration
187187
end
188188

189189
def applicable_to_datetime
@@ -213,16 +213,12 @@ def with_presentation_by_in_grouped_by?
213213

214214
protected
215215

216-
attr_accessor :code, :subscription, :boundaries, :grouped_by_values, :filters, :matching_filters, :ignored_filters, :deduplicate
216+
attr_accessor :code, :context, :boundaries, :grouped_by_values, :filters, :matching_filters, :ignored_filters, :deduplicate
217217

218-
delegate :customer, to: :subscription
218+
delegate :customer, to: :context
219219

220220
def period_duration
221-
@period_duration ||= Subscriptions::DatesService.new_instance(
222-
subscription,
223-
to_datetime + 1.day,
224-
current_usage: subscription.terminated? && subscription.upgraded?
225-
).charges_duration_in_days
221+
@period_duration ||= context.charges_duration_at(to_datetime + 1.day)
226222
end
227223

228224
def build_aggregation_result(row)

0 commit comments

Comments
 (0)