Skip to content

Commit

Permalink
bug(SqlExpression) - Fix batch endpoint (#2921)
Browse files Browse the repository at this point in the history
## Description

* bump Lago-expression gem.
* Create new service Events::CalculateExpressionService.
* Use new service in both Batch and regular event creation.
  • Loading branch information
nudded authored Dec 6, 2024
1 parent 89e55f5 commit 582ff83
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 19 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ gem "valvat", require: false
# Data Export
gem "csv", "~> 3.0"

gem "lago-expression", github: "getlago/lago-expression", glob: "expression-ruby/lago-expression.gemspec"
gem "lago-expression", github: "getlago/lago-expression", glob: "expression-ruby/lago-expression.gemspec", tag: 'v0.1.2'

group :development, :test, :staging do
gem "factory_bot_rails"
Expand Down
7 changes: 4 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
GIT
remote: https://github.com/getlago/lago-expression.git
revision: a096edf11524bfda7d7f196ddb7722b6c5b8eeae
revision: 6883f3472135508101a875edc0a5d077167da592
tag: v0.1.2
glob: expression-ruby/lago-expression.gemspec
specs:
lago-expression (0.0.1)
lago-expression (0.1.0)
bigdecimal
rake (~> 13)
rake-compiler (~> 1.2)
Expand Down Expand Up @@ -693,7 +694,7 @@ GEM
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
rb_sys (0.9.102)
rb_sys (0.9.103)
rbs (3.6.1)
logger
rdoc (6.7.0)
Expand Down
33 changes: 33 additions & 0 deletions app/services/events/calculate_expression_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

module Events
class CalculateExpressionService < BaseService
def initialize(organization:, event:)
@organization = organization
@event = event
super
end

def call
result.event = event

field_name, expression = BillableMetrics::ExpressionCacheService.call(organization.id, event.code) do
bm = organization.billable_metrics.with_expression.find_by(code: event.code)
[bm&.field_name, bm&.expression]
end
return result if expression.blank?

evaluation_event = Lago::Event.new(event.code, event.timestamp.to_i, event.properties)

# The expression can always be parsed, otherwise it would not be saved.
value = Lago::ExpressionParser.parse(expression).evaluate(evaluation_event)
event.properties[field_name] = value

result
end

private

attr_reader :organization, :event
end
end
2 changes: 2 additions & 0 deletions app/services/events/create_batch_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ def validate_events
event.timestamp = Time.zone.at(event_params[:timestamp] ? event_params[:timestamp].to_f : timestamp)
event.precise_total_amount_cents = event_params[:precise_total_amount_cents]

CalculateExpressionService.call(organization:, event:).raise_if_error!

result.events.push(event)
result.errors = result.errors.merge({index => event.errors.messages}) unless event.valid?
end
Expand Down
16 changes: 1 addition & 15 deletions app/services/events/create_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def call
event.timestamp = Time.zone.at(params[:timestamp] ? params[:timestamp].to_f : timestamp)
event.precise_total_amount_cents = params[:precise_total_amount_cents]

pre_ingest(event)
CalculateExpressionService.call(organization:, event:).raise_if_error!

event.save! unless organization.clickhouse_events_store?

Expand All @@ -41,20 +41,6 @@ def call

attr_reader :organization, :params, :timestamp, :metadata

def pre_ingest(event)
field_name, expression = BillableMetrics::ExpressionCacheService.call(organization.id, event.code) do
bm = organization.billable_metrics.with_expression.find_by(code: event.code)
[bm&.field_name, bm&.expression]
end
return if expression.blank?

string_properties = event.properties.transform_values(&:to_s)
evaluation_event = Lago::Event.new(event.code, event.timestamp.to_i, string_properties)

value = Lago::ExpressionParser.parse(expression).evaluate(evaluation_event)
event.properties[field_name] = value
end

def produce_kafka_event(event)
return if ENV['LAGO_KAFKA_BOOTSTRAP_SERVERS'].blank?
return if ENV['LAGO_KAFKA_RAW_EVENTS_TOPIC'].blank?
Expand Down
37 changes: 37 additions & 0 deletions spec/services/events/calculate_expression_service_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe Events::CalculateExpressionService, type: :service do
describe '#call' do
subject(:service_call) { described_class.new(organization: organization, event: event).call }

let(:organization) { create(:organization) }
let(:event) { create(:event, organization: organization, timestamp: Time.current.to_i, code: code, properties: properties) }
let(:code) { 'test_code' }
let(:properties) { {'left' => '1', 'right' => '2'} }
let(:expression) { nil }
let(:field_name) { 'result' }
let(:billable_metric) { create(:billable_metric, organization: organization, code: code, field_name: field_name, expression: expression) }

before do
billable_metric
end

context 'when there is no expression configured' do
it 'does not modify the event properties' do
expect(service_call).to be_success
expect(service_call.event.properties).to eq(properties)
end
end

context 'when an expression is configured for the billable metric' do
let(:expression) { 'event.properties.left + event.properties.right' }

it 'evaluates the expression and updates the event properties' do
expect(service_call).to be_success
expect(service_call.event.properties[field_name]).to eq(3)
end
end
end
end
15 changes: 15 additions & 0 deletions spec/services/events/create_batch_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,21 @@
end
end

context "with an expression configured on the billable metric" do
let(:billable_metric) { create(:billable_metric, code:, organization:, field_name: "result", expression: "concat(event.properties.foo, '-bar')") }

before do
billable_metric
end

it "creates an event and updates the field name with the result of the expression" do
result = create_batch_service.call

expect(result).to be_success
result.events.each { |event| expect(event.properties["result"]).to eq('bar-bar') }
end
end

context 'when timestamp is sent with decimal precision' do
let(:timestamp) { DateTime.parse('2023-09-04T15:45:12.344Z').to_f }

Expand Down

0 comments on commit 582ff83

Please sign in to comment.