diff --git a/app/controllers/api/v1/billable_metrics_controller.rb b/app/controllers/api/v1/billable_metrics_controller.rb index 50f0c12a436..82950f3f003 100644 --- a/app/controllers/api/v1/billable_metrics_controller.rb +++ b/app/controllers/api/v1/billable_metrics_controller.rb @@ -122,6 +122,8 @@ def input_params :recurring, :field_name, :expression, + :rounding_function, + :rounding_precision, filters: [:key, {values: []}] ) end diff --git a/app/graphql/types/billable_metrics/create_input.rb b/app/graphql/types/billable_metrics/create_input.rb index 28a8e00c534..6d7a2036f7a 100644 --- a/app/graphql/types/billable_metrics/create_input.rb +++ b/app/graphql/types/billable_metrics/create_input.rb @@ -12,6 +12,8 @@ class CreateInput < BaseInputObject argument :field_name, String, required: false argument :name, String, required: true argument :recurring, Boolean, required: false + argument :rounding_function, Types::BillableMetrics::RoundingFunctionEnum, required: false + argument :rounding_precision, Integer, required: false argument :weighted_interval, Types::BillableMetrics::WeightedIntervalEnum, required: false argument :filters, [Types::BillableMetricFilters::Input], required: false diff --git a/app/graphql/types/billable_metrics/object.rb b/app/graphql/types/billable_metrics/object.rb index c559ca51973..5baad064a68 100644 --- a/app/graphql/types/billable_metrics/object.rb +++ b/app/graphql/types/billable_metrics/object.rb @@ -27,6 +27,9 @@ class Object < Types::BaseObject field :recurring, Boolean, null: false field :subscriptions_count, Integer, null: false + field :rounding_function, Types::BillableMetrics::RoundingFunctionEnum, null: true + field :rounding_precision, Integer, null: true + field :created_at, GraphQL::Types::ISO8601DateTime, null: false field :deleted_at, GraphQL::Types::ISO8601DateTime, null: true field :updated_at, GraphQL::Types::ISO8601DateTime, null: false diff --git a/app/graphql/types/billable_metrics/rounding_function_enum.rb b/app/graphql/types/billable_metrics/rounding_function_enum.rb new file mode 100644 index 00000000000..1ef3dfad46e --- /dev/null +++ b/app/graphql/types/billable_metrics/rounding_function_enum.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Types + module BillableMetrics + class RoundingFunctionEnum < Types::BaseEnum + BillableMetric::ROUNDING_FUNCTIONS.values.each do |type| + value type + end + end + end +end diff --git a/app/graphql/types/billable_metrics/update_input.rb b/app/graphql/types/billable_metrics/update_input.rb index 9d144a0b25c..2b955d72de0 100644 --- a/app/graphql/types/billable_metrics/update_input.rb +++ b/app/graphql/types/billable_metrics/update_input.rb @@ -14,6 +14,8 @@ class UpdateInput < BaseInputObject argument :field_name, String, required: false argument :name, String, required: true argument :recurring, Boolean, required: false + argument :rounding_function, Types::BillableMetrics::RoundingFunctionEnum, required: false + argument :rounding_precision, Integer, required: false argument :weighted_interval, Types::BillableMetrics::WeightedIntervalEnum, required: false argument :filters, [Types::BillableMetricFilters::Input], required: false diff --git a/app/serializers/v1/billable_metric_serializer.rb b/app/serializers/v1/billable_metric_serializer.rb index a1a3bdcc3ac..0ed3df99b7b 100644 --- a/app/serializers/v1/billable_metric_serializer.rb +++ b/app/serializers/v1/billable_metric_serializer.rb @@ -11,6 +11,8 @@ def serialize aggregation_type: model.aggregation_type, weighted_interval: model.weighted_interval, recurring: model.recurring, + rounding_function: model.rounding_function, + rounding_precision: model.rounding_precision, created_at: model.created_at.iso8601, field_name: model.field_name, expression: model.expression, diff --git a/app/services/billable_metrics/create_service.rb b/app/services/billable_metrics/create_service.rb index a2d18ef458c..1146ba204cc 100644 --- a/app/services/billable_metrics/create_service.rb +++ b/app/services/billable_metrics/create_service.rb @@ -23,6 +23,8 @@ def call recurring: args[:recurring] || false, aggregation_type: args[:aggregation_type]&.to_sym, field_name: args[:field_name], + rounding_function: args[:rounding_function]&.to_sym, + rounding_precision: args[:rounding_precision], weighted_interval: args[:weighted_interval]&.to_sym, expression: args[:expression] ) diff --git a/app/services/billable_metrics/update_service.rb b/app/services/billable_metrics/update_service.rb index cadb15d8df5..5cd57431967 100644 --- a/app/services/billable_metrics/update_service.rb +++ b/app/services/billable_metrics/update_service.rb @@ -38,6 +38,8 @@ def call billable_metric.weighted_interval = params[:weighted_interval]&.to_sym if params.key?(:weighted_interval) billable_metric.field_name = params[:field_name] if params.key?(:field_name) billable_metric.recurring = params[:recurring] if params.key?(:recurring) + billable_metric.rounding_function = params[:rounding_function] if params.key?(:rounding_function) + billable_metric.rounding_precision = params[:rounding_precision] if params.key?(:rounding_precision) billable_metric.weighted_interval = params[:weighted_interval]&.to_sym if params.key?(:weighted_interval) billable_metric.expression = params[:expression] if params.key?(:expression) diff --git a/schema.graphql b/schema.graphql index d3508fa4ef6..9318976098f 100644 --- a/schema.graphql +++ b/schema.graphql @@ -251,6 +251,8 @@ type BillableMetric { organization: Organization plansCount: Int! recurring: Boolean! + roundingFunction: RoundingFunctionEnum + roundingPrecision: Int subscriptionsCount: Int! updatedAt: ISO8601DateTime! weightedInterval: WeightedIntervalEnum @@ -1859,6 +1861,8 @@ input CreateBillableMetricInput { filters: [BillableMetricFiltersInput!] name: String! recurring: Boolean + roundingFunction: RoundingFunctionEnum + roundingPrecision: Int weightedInterval: WeightedIntervalEnum } @@ -6790,6 +6794,12 @@ input RevokeMembershipInput { id: ID! } +enum RoundingFunctionEnum { + ceil + floor + round +} + type SanitizedApiKey { createdAt: ISO8601DateTime! id: ID! @@ -7832,6 +7842,8 @@ input UpdateBillableMetricInput { id: String! name: String! recurring: Boolean + roundingFunction: RoundingFunctionEnum + roundingPrecision: Int weightedInterval: WeightedIntervalEnum } diff --git a/schema.json b/schema.json index 317bf4200e0..85b75cce1f6 100644 --- a/schema.json +++ b/schema.json @@ -2316,6 +2316,34 @@ ] }, + { + "name": "roundingFunction", + "description": null, + "type": { + "kind": "ENUM", + "name": "RoundingFunctionEnum", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "roundingPrecision", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, { "name": "subscriptionsCount", "description": null, @@ -6785,6 +6813,30 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "roundingFunction", + "description": null, + "type": { + "kind": "ENUM", + "name": "RoundingFunctionEnum", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "roundingPrecision", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "weightedInterval", "description": null, @@ -35339,6 +35391,35 @@ ], "enumValues": null }, + { + "kind": "ENUM", + "name": "RoundingFunctionEnum", + "description": null, + "interfaces": null, + "possibleTypes": null, + "fields": null, + "inputFields": null, + "enumValues": [ + { + "name": "round", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ceil", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "floor", + "description": null, + "isDeprecated": false, + "deprecationReason": null + } + ] + }, { "kind": "OBJECT", "name": "SanitizedApiKey", @@ -38374,6 +38455,30 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "roundingFunction", + "description": null, + "type": { + "kind": "ENUM", + "name": "RoundingFunctionEnum", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "roundingPrecision", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "weightedInterval", "description": null, diff --git a/spec/graphql/types/billable_metrics/create_input_spec.rb b/spec/graphql/types/billable_metrics/create_input_spec.rb index 5d9b49b445c..98c7c1f0918 100644 --- a/spec/graphql/types/billable_metrics/create_input_spec.rb +++ b/spec/graphql/types/billable_metrics/create_input_spec.rb @@ -12,6 +12,8 @@ it { is_expected.to accept_argument(:field_name).of_type('String') } it { is_expected.to accept_argument(:name).of_type('String!') } it { is_expected.to accept_argument(:recurring).of_type('Boolean') } + it { is_expected.to accept_argument(:rounding_function).of_type('RoundingFunctionEnum') } + it { is_expected.to accept_argument(:rounding_precision).of_type('Int') } it { is_expected.to accept_argument(:weighted_interval).of_type('WeightedIntervalEnum') } it { is_expected.to accept_argument(:filters).of_type('[BillableMetricFiltersInput!]') } end diff --git a/spec/graphql/types/billable_metrics/object_spec.rb b/spec/graphql/types/billable_metrics/object_spec.rb index 1f83dfd04f6..a253554a4d1 100644 --- a/spec/graphql/types/billable_metrics/object_spec.rb +++ b/spec/graphql/types/billable_metrics/object_spec.rb @@ -24,4 +24,6 @@ it { is_expected.to have_field(:deleted_at).of_type('ISO8601DateTime') } it { is_expected.to have_field(:updated_at).of_type('ISO8601DateTime!') } it { is_expected.to have_field(:integration_mappings).of_type('[Mapping!]') } + it { is_expected.to have_field(:rounding_function).of_type('RoundingFunctionEnum') } + it { is_expected.to have_field(:rounding_precision).of_type('Int') } end diff --git a/spec/graphql/types/billable_metrics/update_input_spec.rb b/spec/graphql/types/billable_metrics/update_input_spec.rb index 6ed7b787555..b963c705513 100644 --- a/spec/graphql/types/billable_metrics/update_input_spec.rb +++ b/spec/graphql/types/billable_metrics/update_input_spec.rb @@ -13,6 +13,8 @@ it { is_expected.to accept_argument(:field_name).of_type('String') } it { is_expected.to accept_argument(:name).of_type('String!') } it { is_expected.to accept_argument(:recurring).of_type('Boolean') } + it { is_expected.to accept_argument(:rounding_function).of_type('RoundingFunctionEnum') } + it { is_expected.to accept_argument(:rounding_precision).of_type('Int') } it { is_expected.to accept_argument(:weighted_interval).of_type('WeightedIntervalEnum') } it { is_expected.to accept_argument(:filters).of_type('[BillableMetricFiltersInput!]') } end diff --git a/spec/requests/api/v1/billable_metrics_controller_spec.rb b/spec/requests/api/v1/billable_metrics_controller_spec.rb index 616b9eae1d4..5388077742c 100644 --- a/spec/requests/api/v1/billable_metrics_controller_spec.rb +++ b/spec/requests/api/v1/billable_metrics_controller_spec.rb @@ -14,7 +14,9 @@ aggregation_type: 'sum_agg', field_name: 'amount_sum', expression: '1 + 2', - recurring: true + recurring: true, + rounding_function: 'round', + rounding_precision: 2 } end @@ -28,6 +30,8 @@ expect(json[:billable_metric][:created_at]).to be_present expect(json[:billable_metric][:recurring]).to eq(create_params[:recurring]) expect(json[:billable_metric][:expression]).to eq(create_params[:expression]) + expect(json[:billable_metric][:rounding_function]).to eq(create_params[:rounding_function]) + expect(json[:billable_metric][:rounding_precision]).to eq(create_params[:rounding_precision]) expect(json[:billable_metric][:filters]).to eq([]) end @@ -49,10 +53,7 @@ expect(response).to have_http_status(:success) expect(json[:billable_metric][:lago_id]).to be_present - expect(json[:billable_metric][:recurring]).to eq( - create_params[:recurring - ] - ) + expect(json[:billable_metric][:recurring]).to eq(create_params[:recurring]) expect(json[:billable_metric][:aggregation_type]).to eq('weighted_sum_agg') expect(json[:billable_metric][:weighted_interval]).to eq('seconds') end diff --git a/spec/serializers/v1/billable_metric_serializer_spec.rb b/spec/serializers/v1/billable_metric_serializer_spec.rb index 71f808d1d8e..90efa38cea6 100644 --- a/spec/serializers/v1/billable_metric_serializer_spec.rb +++ b/spec/serializers/v1/billable_metric_serializer_spec.rb @@ -17,6 +17,8 @@ expect(result['billable_metric']['aggregation_type']).to eq(billable_metric.aggregation_type) expect(result['billable_metric']['field_name']).to eq(billable_metric.field_name) expect(result['billable_metric']['created_at']).to eq(billable_metric.created_at.iso8601) + expect(result['billable_metric']['rounding_function']).to eq(billable_metric.rounding_function) + expect(result['billable_metric']['rounding_precision']).to eq(billable_metric.rounding_precision) expect(result['billable_metric']['weighted_interval']).to eq(billable_metric.weighted_interval) expect(result['billable_metric']['expression']).to eq(billable_metric.expression) expect(result['billable_metric']['active_subscriptions_count']).to eq(0) diff --git a/spec/services/billable_metrics/create_service_spec.rb b/spec/services/billable_metrics/create_service_spec.rb index 95be109d877..4e00015f378 100644 --- a/spec/services/billable_metrics/create_service_spec.rb +++ b/spec/services/billable_metrics/create_service_spec.rb @@ -21,6 +21,8 @@ organization_id: organization.id, aggregation_type: "count_agg", expression: "1 + 2", + rounding_function: "ceil", + rounding_precision: 2, recurring: false } end diff --git a/spec/services/billable_metrics/update_service_spec.rb b/spec/services/billable_metrics/update_service_spec.rb index e5fd9284adf..04dc1aaac69 100644 --- a/spec/services/billable_metrics/update_service_spec.rb +++ b/spec/services/billable_metrics/update_service_spec.rb @@ -16,7 +16,9 @@ description: 'New metric description', aggregation_type: 'sum_agg', field_name: 'field_value', - expression: '1 + 3' + expression: '1 + 3', + rounding_function: 'ceil', + rounding_precision: 2 }.tap do |p| p[:filters] = filters unless filters.nil? end @@ -24,19 +26,20 @@ let(:filters) { nil } describe '#call' do - it 'updates the billable metric' do + it 'updates the billable metric', aggregate_failures: true do result = update_service.call - - aggregate_failures do - expect(result).to be_success - - metric = result.billable_metric - expect(metric.id).to eq(billable_metric.id) - expect(metric.name).to eq('New Metric') - expect(metric.code).to eq('new_metric') - expect(metric.aggregation_type).to eq('sum_agg') - expect(metric.expression).to eq('1 + 3') - end + expect(result).to be_success + + metric = result.billable_metric + expect(metric).to have_attributes( + id: billable_metric.id, + name: 'New Metric', + code: 'new_metric', + aggregation_type: 'sum_agg', + rounding_function: 'ceil', + rounding_precision: 2, + expression: '1 + 3' + ) end context 'with filters arguments' do @@ -106,7 +109,7 @@ before { charge } - it 'updates only name and description' do + it 'updates only name and description', aggregate_failures: true do result = update_service.call aggregate_failures do @@ -120,7 +123,9 @@ expect(result.billable_metric).not_to have_attributes( code: 'new_metric', aggregation_type: 'sum_agg', - field_name: 'field_value' + field_name: 'field_value', + rounding_function: 'ceil', + rounding_precision: 2 ) end end