From b23c289c3f58ac7174048ca2eb6528cd31f43863 Mon Sep 17 00:00:00 2001 From: Vincent Pochet Date: Tue, 23 Aug 2022 14:46:33 +0200 Subject: [PATCH] feat: Update GraphQL schema to create or update charges --- .../charge_model_attributes_handler.rb | 6 + app/graphql/types/charges/input.rb | 3 + app/graphql/types/charges/object.rb | 9 + app/graphql/types/charges/volume_range.rb | 15 ++ .../types/charges/volume_range_input.rb | 15 ++ schema.graphql | 16 ++ schema.json | 194 ++++++++++++++++++ spec/graphql/mutations/plans/create_spec.rb | 67 +++++- spec/graphql/mutations/plans/update_spec.rb | 77 +++++-- 9 files changed, 381 insertions(+), 21 deletions(-) create mode 100644 app/graphql/types/charges/volume_range.rb create mode 100644 app/graphql/types/charges/volume_range_input.rb diff --git a/app/graphql/concerns/charge_model_attributes_handler.rb b/app/graphql/concerns/charge_model_attributes_handler.rb index 827604f1c7e..29454a491ad 100644 --- a/app/graphql/concerns/charge_model_attributes_handler.rb +++ b/app/graphql/concerns/charge_model_attributes_handler.rb @@ -7,6 +7,7 @@ module ChargeModelAttributesHandler # - Graduated model relies on the the list of `GraduatedRange` # - Package model has properties `amount_cents`, `package_size` and `free_units` # - Percentage model has properties `rate`, `fixed_amount`, `free_units_per_events`, `free_units_per_total_aggregation` + # - Volume model has property `ranges` which relies on the list of VolumeRange def prepare_arguments(arguments) return arguments if arguments[:charges].blank? @@ -31,6 +32,10 @@ def prepare_arguments(arguments) free_units_per_events: output[:free_units_per_events], free_units_per_total_aggregation: output[:free_units_per_total_aggregation], } + when :volume + output[:properties] = { + ranges: output[:volume_ranges], + } end # NOTE: delete fields used to build properties @@ -42,6 +47,7 @@ def prepare_arguments(arguments) output.delete(:fixed_amount) output.delete(:free_units_per_events) output.delete(:free_units_per_total_aggregation) + output.delete(:volume_ranges) output end diff --git a/app/graphql/types/charges/input.rb b/app/graphql/types/charges/input.rb index 091317eae34..99e1677cec3 100644 --- a/app/graphql/types/charges/input.rb +++ b/app/graphql/types/charges/input.rb @@ -24,6 +24,9 @@ class Input < Types::BaseInputObject argument :fixed_amount, String, required: false argument :free_units_per_events, Integer, required: false argument :free_units_per_total_aggregation, String, required: false + + # NOTE: Volume charge model + argument :volume_ranges, [Types::Charges::VolumeRangeInput], required: false end end end diff --git a/app/graphql/types/charges/object.rb b/app/graphql/types/charges/object.rb index e46cd2d2baa..25695b09067 100644 --- a/app/graphql/types/charges/object.rb +++ b/app/graphql/types/charges/object.rb @@ -28,6 +28,9 @@ class Object < Types::BaseObject field :free_units_per_events, Integer, null: true field :free_units_per_total_aggregation, String, null: true + # NOTE: Volume charge model + field :volume_ranges, [Types::Charges::VolumeRange], null: true + def amount return unless object.standard? || object.package? @@ -75,6 +78,12 @@ def free_units_per_total_aggregation object.properties['free_units_per_total_aggregation'] end + + def volume_ranges + return unless object.volume? + + object.properties['ranges'] + end end end end diff --git a/app/graphql/types/charges/volume_range.rb b/app/graphql/types/charges/volume_range.rb new file mode 100644 index 00000000000..f6696a91793 --- /dev/null +++ b/app/graphql/types/charges/volume_range.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module Charges + class VolumeRange < Types::BaseObject + graphql_name 'VolumeRange' + + field :from_value, Integer, null: false + field :to_value, Integer, null: true + + field :per_unit_amount, String, null: false + field :flat_amount, String, null: false + end + end +end diff --git a/app/graphql/types/charges/volume_range_input.rb b/app/graphql/types/charges/volume_range_input.rb new file mode 100644 index 00000000000..69353dd94e9 --- /dev/null +++ b/app/graphql/types/charges/volume_range_input.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module Charges + class VolumeRangeInput < Types::BaseInputObject + graphql_name 'VolumeRangeInput' + + argument :from_value, Integer, required: true + argument :to_value, Integer, required: false + + argument :per_unit_amount, String, required: true + argument :flat_amount, String, required: true + end + end +end diff --git a/schema.graphql b/schema.graphql index dcb3f5ff5be..25e56a1fdea 100644 --- a/schema.graphql +++ b/schema.graphql @@ -147,6 +147,7 @@ type Charge { packageSize: Int rate: String updatedAt: ISO8601DateTime! + volumeRanges: [VolumeRange!] } input ChargeInput { @@ -161,6 +162,7 @@ input ChargeInput { id: ID packageSize: Int rate: String + volumeRanges: [VolumeRangeInput!] } enum ChargeModelEnum { @@ -3618,6 +3620,20 @@ type User { updatedAt: ISO8601DateTime! } +type VolumeRange { + flatAmount: String! + fromValue: Int! + perUnitAmount: String! + toValue: Int +} + +input VolumeRangeInput { + flatAmount: String! + fromValue: Int! + perUnitAmount: String! + toValue: Int +} + type Wallet { balance: String! consumedAmount: String! diff --git a/schema.json b/schema.json index dc5900c5dac..34ab566cc8e 100644 --- a/schema.json +++ b/schema.json @@ -1457,6 +1457,28 @@ "deprecationReason": null, "args": [ + ] + }, + { + "name": "volumeRanges", + "description": null, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "VolumeRange", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + ] } ], @@ -1618,6 +1640,26 @@ "defaultValue": null, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "volumeRanges", + "description": null, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "VolumeRangeInput", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null } ], "enumValues": null @@ -13605,6 +13647,158 @@ "inputFields": null, "enumValues": null }, + { + "kind": "OBJECT", + "name": "VolumeRange", + "description": null, + "interfaces": [ + + ], + "possibleTypes": null, + "fields": [ + { + "name": "flatAmount", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "fromValue", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "perUnitAmount", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "toValue", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + } + ], + "inputFields": null, + "enumValues": null + }, + { + "kind": "INPUT_OBJECT", + "name": "VolumeRangeInput", + "description": null, + "interfaces": null, + "possibleTypes": null, + "fields": null, + "inputFields": [ + { + "name": "fromValue", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "toValue", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "perUnitAmount", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "flatAmount", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "enumValues": null + }, { "kind": "OBJECT", "name": "Wallet", diff --git a/spec/graphql/mutations/plans/create_spec.rb b/spec/graphql/mutations/plans/create_spec.rb index 5fc1e12ac47..9d9f142ebc2 100644 --- a/spec/graphql/mutations/plans/create_spec.rb +++ b/spec/graphql/mutations/plans/create_spec.rb @@ -18,8 +18,17 @@ amountCurrency, charges { id, + chargeModel, billableMetric { id name code }, - graduatedRanges { fromValue, toValue } + amount, + freeUnits, + packageSize, + rate, + fixedAmount, + freeUnitsPerEvents, + freeUnitsPerTotalAggregation, + graduatedRanges { fromValue, toValue }, + volumeRanges { fromValue, toValue } } } } @@ -27,7 +36,7 @@ end let(:billable_metrics) do - create_list(:billable_metric, 4, organization: organization) + create_list(:billable_metric, 5, organization: organization) end it 'creates a plan' do @@ -45,19 +54,19 @@ amountCurrency: 'EUR', charges: [ { - billableMetricId: billable_metrics.first.id, + billableMetricId: billable_metrics[0].id, amount: '100.00', chargeModel: 'standard', }, { - billableMetricId: billable_metrics.second.id, + billableMetricId: billable_metrics[1].id, chargeModel: 'package', amount: '300.00', freeUnits: 10, packageSize: 10, }, { - billableMetricId: billable_metrics.third.id, + billableMetricId: billable_metrics[2].id, chargeModel: 'percentage', rate: '0.25', fixedAmount: '2', @@ -65,7 +74,7 @@ freeUnitsPerTotalAggregation: '50', }, { - billableMetricId: billable_metrics.last.id, + billableMetricId: billable_metrics[3].id, chargeModel: 'graduated', graduatedRanges: [ { @@ -82,6 +91,24 @@ }, ], }, + { + billableMetricId: billable_metrics[4].id, + chargeModel: 'volume', + volumeRanges: [ + { + fromValue: 0, + toValue: 10, + perUnitAmount: '2.00', + flatAmount: '0', + }, + { + fromValue: 11, + toValue: nil, + perUnitAmount: '3.00', + flatAmount: '3.00', + }, + ], + }, ], }, }, @@ -97,8 +124,32 @@ expect(result_data['payInAdvance']).to eq(false) expect(result_data['amountCents']).to eq(200) expect(result_data['amountCurrency']).to eq('EUR') - expect(result_data['charges'].count).to eq(4) - expect(result_data['charges'][3]['graduatedRanges'].count).to eq(2) + expect(result_data['charges'].count).to eq(5) + + standard_charge = result_data['charges'][0] + expect(standard_charge['amount']).to eq('100.00') + expect(standard_charge['chargeModel']).to eq('standard') + + package_charge = result_data['charges'][1] + expect(package_charge['chargeModel']).to eq('package') + expect(package_charge['amount']).to eq('300.00') + expect(package_charge['freeUnits']).to eq(10) + expect(package_charge['packageSize']).to eq(10) + + percentage_charge = result_data['charges'][2] + expect(percentage_charge['chargeModel']).to eq('percentage') + expect(percentage_charge['rate']).to eq('0.25') + expect(percentage_charge['fixedAmount']).to eq('2') + expect(percentage_charge['freeUnitsPerEvents']).to eq(5) + expect(percentage_charge['freeUnitsPerTotalAggregation']).to eq('50') + + graduated_charge = result_data['charges'][3] + expect(graduated_charge['chargeModel']).to eq('graduated') + expect(graduated_charge['graduatedRanges'].count).to eq(2) + + volume_charge = result_data['charges'][4] + expect(volume_charge['chargeModel']).to eq('volume') + expect(volume_charge['volumeRanges'].count).to eq(2) end end diff --git a/spec/graphql/mutations/plans/update_spec.rb b/spec/graphql/mutations/plans/update_spec.rb index f825c677b90..3111956aaad 100644 --- a/spec/graphql/mutations/plans/update_spec.rb +++ b/spec/graphql/mutations/plans/update_spec.rb @@ -19,8 +19,17 @@ amountCurrency, charges { id, + chargeModel, billableMetric { id name code }, - graduatedRanges { fromValue, toValue } + amount, + freeUnits, + packageSize, + rate, + fixedAmount, + freeUnitsPerEvents, + freeUnitsPerTotalAggregation, + graduatedRanges { fromValue, toValue }, + volumeRanges { fromValue, toValue } } } } @@ -28,7 +37,7 @@ end let(:billable_metrics) do - create_list(:billable_metric, 4, organization: organization) + create_list(:billable_metric, 5, organization: organization) end it 'updates a plan' do @@ -46,19 +55,19 @@ amountCurrency: 'EUR', charges: [ { - billableMetricId: billable_metrics.first.id, - amount: '100', + billableMetricId: billable_metrics[0].id, + amount: '100.00', chargeModel: 'standard', }, { - billableMetricId: billable_metrics.second.id, + billableMetricId: billable_metrics[1].id, chargeModel: 'package', - amount: '300', + amount: '300.00', freeUnits: 10, packageSize: 10, }, { - billableMetricId: billable_metrics.third.id, + billableMetricId: billable_metrics[2].id, chargeModel: 'percentage', rate: '0.25', fixedAmount: '2', @@ -66,20 +75,38 @@ freeUnitsPerTotalAggregation: '50', }, { - billableMetricId: billable_metrics.last.id, + billableMetricId: billable_metrics[3].id, chargeModel: 'graduated', graduatedRanges: [ { fromValue: 0, toValue: 10, - perUnitAmount: '2', + perUnitAmount: '2.00', flatAmount: '0', }, { fromValue: 11, toValue: nil, - perUnitAmount: '3', - flatAmount: '3', + perUnitAmount: '3.00', + flatAmount: '3.00', + }, + ], + }, + { + billableMetricId: billable_metrics[4].id, + chargeModel: 'volume', + volumeRanges: [ + { + fromValue: 0, + toValue: 10, + perUnitAmount: '2.00', + flatAmount: '0', + }, + { + fromValue: 11, + toValue: nil, + perUnitAmount: '3.00', + flatAmount: '3.00', }, ], }, @@ -98,8 +125,32 @@ expect(result_data['payInAdvance']).to eq(false) expect(result_data['amountCents']).to eq(200) expect(result_data['amountCurrency']).to eq('EUR') - expect(result_data['charges'].count).to eq(4) - expect(result_data['charges'][3]['graduatedRanges'].count).to eq(2) + expect(result_data['charges'].count).to eq(5) + + standard_charge = result_data['charges'][0] + expect(standard_charge['amount']).to eq('100.00') + expect(standard_charge['chargeModel']).to eq('standard') + + package_charge = result_data['charges'][1] + expect(package_charge['chargeModel']).to eq('package') + expect(package_charge['amount']).to eq('300.00') + expect(package_charge['freeUnits']).to eq(10) + expect(package_charge['packageSize']).to eq(10) + + percentage_charge = result_data['charges'][2] + expect(percentage_charge['chargeModel']).to eq('percentage') + expect(percentage_charge['rate']).to eq('0.25') + expect(percentage_charge['fixedAmount']).to eq('2') + expect(percentage_charge['freeUnitsPerEvents']).to eq(5) + expect(percentage_charge['freeUnitsPerTotalAggregation']).to eq('50') + + graduated_charge = result_data['charges'][3] + expect(graduated_charge['chargeModel']).to eq('graduated') + expect(graduated_charge['graduatedRanges'].count).to eq(2) + + volume_charge = result_data['charges'][4] + expect(volume_charge['chargeModel']).to eq('volume') + expect(volume_charge['volumeRanges'].count).to eq(2) end end