From 7ff89cac80a7358534185779ed4992b755919d97 Mon Sep 17 00:00:00 2001
From: fern-api <115122769+fern-api[bot]@users.noreply.github.com>
Date: Mon, 22 Jun 2026 12:02:23 +0000
Subject: [PATCH 1/3] [fern-generated] Update SDK
Generated by Fern
CLI Version: unknown
Generators:
- fernapi/fern-ruby-sdk: 1.1.13
---
.fern/metadata.json | 2 +-
lib/schematic.rb | 1 +
lib/schematic/billing/client.rb | 8 ++++++--
.../types/list_billing_prices_params.rb | 1 +
.../types/list_billing_prices_request.rb | 1 +
.../list_billing_product_prices_params.rb | 1 +
.../list_billing_product_prices_request.rb | 1 +
.../types/account_member_permission.rb | 1 -
.../types/check_flags_response_data.rb | 1 +
lib/schematic/types/company_credit_balance.rb | 11 +++++++++++
.../company_subscription_response_data.rb | 1 +
lib/schematic/types/feature_entitlement.rb | 2 ++
.../types/rulesengine_feature_entitlement.rb | 2 ++
reference.md | 18 ++++++++++++++++++
14 files changed, 47 insertions(+), 4 deletions(-)
create mode 100644 lib/schematic/types/company_credit_balance.rb
diff --git a/.fern/metadata.json b/.fern/metadata.json
index f62fc83..cb6e7f3 100644
--- a/.fern/metadata.json
+++ b/.fern/metadata.json
@@ -13,6 +13,6 @@
"webrick": ">= 1.0"
}
},
- "originGitCommit": "786b643cc0bf17aff1114597108246b5cf8af395",
+ "originGitCommit": "df83a2723a19055c70199e3b0b78b77e9c394838",
"sdkVersion": "1.4.5"
}
\ No newline at end of file
diff --git a/lib/schematic.rb b/lib/schematic.rb
index 850890b..7970d21 100644
--- a/lib/schematic.rb
+++ b/lib/schematic.rb
@@ -500,6 +500,7 @@
require_relative "schematic/features/types/update_flag_rules_response"
require_relative "schematic/types/check_flag_response_data"
require_relative "schematic/features/types/check_flag_response"
+require_relative "schematic/types/company_credit_balance"
require_relative "schematic/types/trial_status"
require_relative "schematic/types/datastream_company_plan"
require_relative "schematic/types/check_flags_response_data"
diff --git a/lib/schematic/billing/client.rb b/lib/schematic/billing/client.rb
index 9309574..6fab89a 100644
--- a/lib/schematic/billing/client.rb
+++ b/lib/schematic/billing/client.rb
@@ -487,6 +487,7 @@ def delete_payment_method_by_external_id(request_options: {}, **params)
# @option params [String, nil] :ids
# @option params [String, nil] :interval
# @option params [Boolean, nil] :is_active
+ # @option params [String, nil] :plan_version_id
# @option params [Integer, nil] :price
# @option params [String, nil] :product_id
# @option params [String, nil] :product_ids
@@ -501,7 +502,7 @@ def delete_payment_method_by_external_id(request_options: {}, **params)
# @return [Schematic::Billing::Types::ListBillingPricesResponse]
def list_billing_prices(request_options: {}, **params)
params = Schematic::Internal::Types::Utils.normalize_keys(params)
- query_param_names = %i[currency for_initial_plan for_trial_expiry_plan ids interval is_active price product_id product_ids provider_type q tiers_mode usage_type with_meter limit offset]
+ query_param_names = %i[currency for_initial_plan for_trial_expiry_plan ids interval is_active plan_version_id price product_id product_ids provider_type q tiers_mode usage_type with_meter limit offset]
query_params = {}
query_params["currency"] = params[:currency] if params.key?(:currency)
query_params["for_initial_plan"] = params[:for_initial_plan] if params.key?(:for_initial_plan)
@@ -509,6 +510,7 @@ def list_billing_prices(request_options: {}, **params)
query_params["ids"] = params[:ids] if params.key?(:ids)
query_params["interval"] = params[:interval] if params.key?(:interval)
query_params["is_active"] = params[:is_active] if params.key?(:is_active)
+ query_params["plan_version_id"] = params[:plan_version_id] if params.key?(:plan_version_id)
query_params["price"] = params[:price] if params.key?(:price)
query_params["product_id"] = params[:product_id] if params.key?(:product_id)
query_params["product_ids"] = params[:product_ids] if params.key?(:product_ids)
@@ -619,6 +621,7 @@ def delete_billing_product(request_options: {}, **params)
# @option params [String, nil] :ids
# @option params [String, nil] :interval
# @option params [Boolean, nil] :is_active
+ # @option params [String, nil] :plan_version_id
# @option params [Integer, nil] :price
# @option params [String, nil] :product_id
# @option params [String, nil] :product_ids
@@ -633,7 +636,7 @@ def delete_billing_product(request_options: {}, **params)
# @return [Schematic::Billing::Types::ListBillingProductPricesResponse]
def list_billing_product_prices(request_options: {}, **params)
params = Schematic::Internal::Types::Utils.normalize_keys(params)
- query_param_names = %i[currency for_initial_plan for_trial_expiry_plan ids interval is_active price product_id product_ids provider_type q tiers_mode usage_type with_meter limit offset]
+ query_param_names = %i[currency for_initial_plan for_trial_expiry_plan ids interval is_active plan_version_id price product_id product_ids provider_type q tiers_mode usage_type with_meter limit offset]
query_params = {}
query_params["currency"] = params[:currency] if params.key?(:currency)
query_params["for_initial_plan"] = params[:for_initial_plan] if params.key?(:for_initial_plan)
@@ -641,6 +644,7 @@ def list_billing_product_prices(request_options: {}, **params)
query_params["ids"] = params[:ids] if params.key?(:ids)
query_params["interval"] = params[:interval] if params.key?(:interval)
query_params["is_active"] = params[:is_active] if params.key?(:is_active)
+ query_params["plan_version_id"] = params[:plan_version_id] if params.key?(:plan_version_id)
query_params["price"] = params[:price] if params.key?(:price)
query_params["product_id"] = params[:product_id] if params.key?(:product_id)
query_params["product_ids"] = params[:product_ids] if params.key?(:product_ids)
diff --git a/lib/schematic/billing/types/list_billing_prices_params.rb b/lib/schematic/billing/types/list_billing_prices_params.rb
index 9919813..d94a838 100644
--- a/lib/schematic/billing/types/list_billing_prices_params.rb
+++ b/lib/schematic/billing/types/list_billing_prices_params.rb
@@ -13,6 +13,7 @@ class ListBillingPricesParams < Internal::Types::Model
field :is_active, -> { Internal::Types::Boolean }, optional: true, nullable: false
field :limit, -> { Integer }, optional: true, nullable: false
field :offset, -> { Integer }, optional: true, nullable: false
+ field :plan_version_id, -> { String }, optional: true, nullable: false
field :price, -> { Integer }, optional: true, nullable: false
field :product_id, -> { String }, optional: true, nullable: false
field :product_ids, -> { Internal::Types::Array[String] }, optional: true, nullable: false
diff --git a/lib/schematic/billing/types/list_billing_prices_request.rb b/lib/schematic/billing/types/list_billing_prices_request.rb
index 14c1123..9c86a3b 100644
--- a/lib/schematic/billing/types/list_billing_prices_request.rb
+++ b/lib/schematic/billing/types/list_billing_prices_request.rb
@@ -10,6 +10,7 @@ class ListBillingPricesRequest < Internal::Types::Model
field :ids, -> { String }, optional: true, nullable: false
field :interval, -> { String }, optional: true, nullable: false
field :is_active, -> { Internal::Types::Boolean }, optional: true, nullable: false
+ field :plan_version_id, -> { String }, optional: true, nullable: false
field :price, -> { Integer }, optional: true, nullable: false
field :product_id, -> { String }, optional: true, nullable: false
field :product_ids, -> { String }, optional: true, nullable: false
diff --git a/lib/schematic/billing/types/list_billing_product_prices_params.rb b/lib/schematic/billing/types/list_billing_product_prices_params.rb
index 5057168..8499c91 100644
--- a/lib/schematic/billing/types/list_billing_product_prices_params.rb
+++ b/lib/schematic/billing/types/list_billing_product_prices_params.rb
@@ -13,6 +13,7 @@ class ListBillingProductPricesParams < Internal::Types::Model
field :is_active, -> { Internal::Types::Boolean }, optional: true, nullable: false
field :limit, -> { Integer }, optional: true, nullable: false
field :offset, -> { Integer }, optional: true, nullable: false
+ field :plan_version_id, -> { String }, optional: true, nullable: false
field :price, -> { Integer }, optional: true, nullable: false
field :product_id, -> { String }, optional: true, nullable: false
field :product_ids, -> { Internal::Types::Array[String] }, optional: true, nullable: false
diff --git a/lib/schematic/billing/types/list_billing_product_prices_request.rb b/lib/schematic/billing/types/list_billing_product_prices_request.rb
index be90c06..f6e4d5c 100644
--- a/lib/schematic/billing/types/list_billing_product_prices_request.rb
+++ b/lib/schematic/billing/types/list_billing_product_prices_request.rb
@@ -10,6 +10,7 @@ class ListBillingProductPricesRequest < Internal::Types::Model
field :ids, -> { String }, optional: true, nullable: false
field :interval, -> { String }, optional: true, nullable: false
field :is_active, -> { Internal::Types::Boolean }, optional: true, nullable: false
+ field :plan_version_id, -> { String }, optional: true, nullable: false
field :price, -> { Integer }, optional: true, nullable: false
field :product_id, -> { String }, optional: true, nullable: false
field :product_ids, -> { String }, optional: true, nullable: false
diff --git a/lib/schematic/types/account_member_permission.rb b/lib/schematic/types/account_member_permission.rb
index 7f50e4e..55eb18b 100644
--- a/lib/schematic/types/account_member_permission.rb
+++ b/lib/schematic/types/account_member_permission.rb
@@ -5,7 +5,6 @@ module Types
module AccountMemberPermission
extend Schematic::Internal::Types::Enum
- BILLING_CREDITS_EDIT = "billing_credits_edit"
COMPANIES_EDIT = "companies_edit"
COMPANY_USERS_EDIT = "company_users_edit"
COMPONENTS_EDIT = "components_edit"
diff --git a/lib/schematic/types/check_flags_response_data.rb b/lib/schematic/types/check_flags_response_data.rb
index fcb8c97..8e3da0e 100644
--- a/lib/schematic/types/check_flags_response_data.rb
+++ b/lib/schematic/types/check_flags_response_data.rb
@@ -3,6 +3,7 @@
module Schematic
module Types
class CheckFlagsResponseData < Internal::Types::Model
+ field :credit_balances, -> { Internal::Types::Hash[String, Schematic::Types::CompanyCreditBalance] }, optional: true, nullable: false
field :flags, -> { Internal::Types::Array[Schematic::Types::CheckFlagResponseData] }, optional: false, nullable: false
field :plan, -> { Schematic::Types::DatastreamCompanyPlan }, optional: true, nullable: false
end
diff --git a/lib/schematic/types/company_credit_balance.rb b/lib/schematic/types/company_credit_balance.rb
new file mode 100644
index 0000000..80aeba8
--- /dev/null
+++ b/lib/schematic/types/company_credit_balance.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Schematic
+ module Types
+ class CompanyCreditBalance < Internal::Types::Model
+ field :remaining, -> { Integer }, optional: false, nullable: false
+ field :reserved, -> { Integer }, optional: false, nullable: false
+ field :settled, -> { Integer }, optional: false, nullable: false
+ end
+ end
+end
diff --git a/lib/schematic/types/company_subscription_response_data.rb b/lib/schematic/types/company_subscription_response_data.rb
index 4ddcbf5..86c0588 100644
--- a/lib/schematic/types/company_subscription_response_data.rb
+++ b/lib/schematic/types/company_subscription_response_data.rb
@@ -10,6 +10,7 @@ class CompanySubscriptionResponseData < Internal::Types::Model
field :discounts, -> { Internal::Types::Array[Schematic::Types::BillingSubscriptionDiscountView] }, optional: false, nullable: false
field :expired_at, -> { String }, optional: true, nullable: false
field :interval, -> { String }, optional: false, nullable: false
+ field :is_initial, -> { Internal::Types::Boolean }, optional: false, nullable: false
field :latest_invoice, -> { Schematic::Types::InvoiceResponseData }, optional: true, nullable: false
field :payment_method, -> { Schematic::Types::PaymentMethodResponseData }, optional: true, nullable: false
field :products, -> { Internal::Types::Array[Schematic::Types::BillingProductForSubscriptionResponseData] }, optional: false, nullable: false
diff --git a/lib/schematic/types/feature_entitlement.rb b/lib/schematic/types/feature_entitlement.rb
index 50844fa..effb983 100644
--- a/lib/schematic/types/feature_entitlement.rb
+++ b/lib/schematic/types/feature_entitlement.rb
@@ -4,6 +4,7 @@ module Schematic
module Types
class FeatureEntitlement < Internal::Types::Model
field :allocation, -> { Integer }, optional: true, nullable: false
+ field :consumption_rate, -> { Integer }, optional: true, nullable: false
field :credit_id, -> { String }, optional: true, nullable: false
field :credit_remaining, -> { Integer }, optional: true, nullable: false
field :credit_reserved, -> { Integer }, optional: true, nullable: false
@@ -11,6 +12,7 @@ class FeatureEntitlement < Internal::Types::Model
field :credit_total, -> { Integer }, optional: true, nullable: false
field :credit_used, -> { Integer }, optional: true, nullable: false
field :event_name, -> { String }, optional: true, nullable: false
+ field :event_subtype, -> { String }, optional: true, nullable: false
field :feature_id, -> { String }, optional: false, nullable: false
field :feature_key, -> { String }, optional: false, nullable: false
field :metric_period, -> { Schematic::Types::MetricPeriod }, optional: true, nullable: false
diff --git a/lib/schematic/types/rulesengine_feature_entitlement.rb b/lib/schematic/types/rulesengine_feature_entitlement.rb
index 0e1ba7a..45e57e8 100644
--- a/lib/schematic/types/rulesengine_feature_entitlement.rb
+++ b/lib/schematic/types/rulesengine_feature_entitlement.rb
@@ -4,6 +4,7 @@ module Schematic
module Types
class RulesengineFeatureEntitlement < Internal::Types::Model
field :allocation, -> { Integer }, optional: true, nullable: false
+ field :consumption_rate, -> { Integer }, optional: true, nullable: false
field :credit_id, -> { String }, optional: true, nullable: false
field :credit_remaining, -> { Integer }, optional: true, nullable: false
field :credit_reserved, -> { Integer }, optional: true, nullable: false
@@ -11,6 +12,7 @@ class RulesengineFeatureEntitlement < Internal::Types::Model
field :credit_total, -> { Integer }, optional: true, nullable: false
field :credit_used, -> { Integer }, optional: true, nullable: false
field :event_name, -> { String }, optional: true, nullable: false
+ field :event_subtype, -> { String }, optional: true, nullable: false
field :feature_id, -> { String }, optional: false, nullable: false
field :feature_key, -> { String }, optional: false, nullable: false
field :metric_period, -> { Schematic::Types::RulesengineMetricPeriod }, optional: true, nullable: false
diff --git a/reference.md b/reference.md
index 3155b8c..468b011 100644
--- a/reference.md
+++ b/reference.md
@@ -2419,6 +2419,7 @@ client.billing.list_billing_prices(
ids: ["ids"],
interval: "interval",
is_active: true,
+ plan_version_id: "plan_version_id",
price: 1000000,
product_id: "product_id",
product_ids: ["product_ids"],
@@ -2492,6 +2493,14 @@ client.billing.list_billing_prices(
-
+**plan_version_id:** `String` — Filter for prices belonging to a specific plan version (e.g. the latest published version)
+
+
+
+
+
+-
+
**price:** `Integer`
@@ -2841,6 +2850,7 @@ client.billing.list_billing_product_prices(
ids: ["ids"],
interval: "interval",
is_active: true,
+ plan_version_id: "plan_version_id",
price: 1000000,
product_id: "product_id",
product_ids: ["product_ids"],
@@ -2914,6 +2924,14 @@ client.billing.list_billing_product_prices(
-
+**plan_version_id:** `String` — Filter for prices belonging to a specific plan version (e.g. the latest published version)
+
+
+
+
+
+-
+
**price:** `Integer`
From 6a363d7952a9d0d1c2a6a0f89edc84ac94a879bb Mon Sep 17 00:00:00 2001
From: fern-api <115122769+fern-api[bot]@users.noreply.github.com>
Date: Mon, 22 Jun 2026 12:02:26 +0000
Subject: [PATCH 2/3] [fern-replay] Applied customizations
Patches with unresolved conflicts (2):
- patch-6a2db2b5: handle partial messages properly
- patch-46462128: add tests and address nits
Run `fern-replay resolve` to apply these customizations.
---
.fern/replay.lock | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/.fern/replay.lock b/.fern/replay.lock
index 1fd5bfe..0159d36 100644
--- a/.fern/replay.lock
+++ b/.fern/replay.lock
@@ -18,7 +18,13 @@ generations:
cli_version: unknown
generator_versions:
fernapi/fern-ruby-sdk: 1.1.13
-current_generation: 3f7bc10e75ced0491ab79b97ad6728099789257f
+ - commit_sha: 7ff89cac80a7358534185779ed4992b755919d97
+ tree_hash: 0067d3b0febf9fcc16a4e73c0b6df32358a38c52
+ timestamp: 2026-06-22T12:02:24.084Z
+ cli_version: unknown
+ generator_versions:
+ fernapi/fern-ruby-sdk: 1.1.13
+current_generation: 7ff89cac80a7358534185779ed4992b755919d97
patches:
- id: patch-6a2db2b5
content_hash: sha256:630a8c3df9ae971e973e4dcb3d71d0d9f28b8da9877b0ff27ff3cca2a1eb924a
From 84f6ca515be94923d7043c99bda3cb0ddbea5558 Mon Sep 17 00:00:00 2001
From: Christopher Brady
Date: Mon, 22 Jun 2026 11:09:07 -0600
Subject: [PATCH 3/3] [fern-replay] Resolved conflicts
Patches replayed:
- patch-46462128: add tests and address nits
---
.fern/replay.lock | 320 ++----------------------------
lib/schematic/datastream/merge.rb | 7 +-
2 files changed, 17 insertions(+), 310 deletions(-)
diff --git a/.fern/replay.lock b/.fern/replay.lock
index 0159d36..2fdceff 100644
--- a/.fern/replay.lock
+++ b/.fern/replay.lock
@@ -26,330 +26,40 @@ generations:
fernapi/fern-ruby-sdk: 1.1.13
current_generation: 7ff89cac80a7358534185779ed4992b755919d97
patches:
- - id: patch-6a2db2b5
- content_hash: sha256:630a8c3df9ae971e973e4dcb3d71d0d9f28b8da9877b0ff27ff3cca2a1eb924a
- original_commit: 6a2db2b584601e13f90302155d37a22e45542fab
- original_message: handle partial messages properly
- original_author: Christopher Brady
- base_generation: e75de47f4e4f08f8e683366dd9b3936bc5c6452f
- files:
- - lib/schematic/datastream/merge.rb
- patch_content: |
- diff --git a/lib/schematic/datastream/merge.rb b/lib/schematic/datastream/merge.rb
- index 0ab68fe..7f1084d 100644
- --- a/lib/schematic/datastream/merge.rb
- +++ b/lib/schematic/datastream/merge.rb
- @@ -16,25 +16,42 @@ module Schematic
- COMPANY_MAP_FIELDS = %i[credit_balances keys traits].freeze
- COMPANY_ARRAY_FIELDS = %i[billing_product_ids entitlements plan_ids plan_version_ids rules].freeze
-
- + # Partials don't carry refreshed entitlements, so when their derived
- + # fields change in another part of the company we sync them here to match
- + # server behavior (see schematic-python merge.partial_company):
- + # - credit_remaining <- credit_balances[credit_id]
- + # - usage <- metric value matching (event_name, metric_period, month_reset)
- + # Both are skipped when the partial also sends entitlements wholesale.
- def partial_company(existing, partial_data)
- return existing unless partial_data.is_a?(Hash)
-
- result = deep_copy(existing)
- + entitlements_in_partial = partial_data.key?(:entitlements) || partial_data.key?("entitlements")
- + updated_balances = nil
- + metrics_updated = false
-
- partial_data.each do |key, value|
- sym_key = key.to_sym
- if COMPANY_MAP_FIELDS.include?(sym_key)
- result[sym_key] ||= {}
- result[sym_key] = result[sym_key].merge(value) if value.is_a?(Hash)
- + updated_balances = (value.is_a?(Hash) ? value : {}) if sym_key == :credit_balances
- elsif COMPANY_ARRAY_FIELDS.include?(sym_key)
- result[sym_key] = value if value.is_a?(Array)
- elsif sym_key == :metrics
- result[sym_key] = upsert_metrics(result[sym_key] || [], value || [])
- + metrics_updated = true
- else
- result[sym_key] = value
- end
- end
-
- + if (updated_balances || metrics_updated) && !entitlements_in_partial
- + result[:entitlements] = sync_entitlements(
- + result[:entitlements], result[:metrics], updated_balances, metrics_updated
- + )
- + end
- +
- result
- end
-
- @@ -90,6 +107,60 @@ module Schematic
- def get_metric_field(metric, field)
- metric[field] || metric[field.to_s]
- end
- +
- + # Re-derive entitlement usage / credit_remaining from the merged metrics
- + # and the just-updated credit balances. Mirrors schematic-python so that
- + # entitlement usage reflects DataStream track events immediately.
- + def sync_entitlements(entitlements, metrics, updated_balances, metrics_updated)
- + return entitlements unless entitlements.is_a?(Array) && !entitlements.empty?
- +
- + metrics_lookup = {}
- + if metrics_updated && metrics.is_a?(Array)
- + metrics.each do |metric|
- + next unless metric.is_a?(Hash)
- +
- + key = [
- + get_metric_field(metric, :event_subtype) || "",
- + get_metric_field(metric, :period) || "",
- + get_metric_field(metric, :month_reset) || ""
- + ]
- + value = get_metric_field(metric, :value)
- + metrics_lookup[key] = value.nil? ? 0 : value
- + end
- + end
- +
- + entitlements.map do |ent|
- + next ent unless ent.is_a?(Hash)
- +
- + new_ent = deep_copy(ent)
- +
- + credit_id = get_metric_field(ent, :credit_id)
- + if updated_balances && credit_id
- + present, balance = fetch_balance(updated_balances, credit_id)
- + new_ent[:credit_remaining] = balance if present
- + end
- +
- + event_name = get_metric_field(ent, :event_name)
- + unless metrics_lookup.empty? || event_name.nil?
- + period = get_metric_field(ent, :metric_period) || "all_time"
- + month_reset = get_metric_field(ent, :month_reset) || "first_of_month"
- + matched = metrics_lookup[[event_name, period, month_reset]]
- + new_ent[:usage] = matched unless matched.nil?
- + end
- +
- + new_ent
- + end
- + end
- +
- + # credit_balances keys may be symbols (deep_copy symbolizes) while an
- + # entitlement's credit_id is always a string, so check both forms.
- + def fetch_balance(balances, credit_id)
- + return [true, balances[credit_id]] if balances.key?(credit_id)
- + return [true, balances[credit_id.to_sym]] if balances.key?(credit_id.to_sym)
- + return [true, balances[credit_id.to_s]] if balances.key?(credit_id.to_s)
- +
- + [false, nil]
- + end
- end
- end
- end
- theirs_snapshot:
- lib/schematic/datastream/merge.rb: |
- # frozen_string_literal: true
-
- require "json"
-
- module Schematic
- module DataStream
- module Merge
- module_function
-
- def deep_copy(obj)
- JSON.parse(JSON.generate(obj), symbolize_names: true)
- rescue StandardError
- obj.dup
- end
-
- COMPANY_MAP_FIELDS = %i[credit_balances keys traits].freeze
- COMPANY_ARRAY_FIELDS = %i[billing_product_ids entitlements plan_ids plan_version_ids rules].freeze
-
- # Partials don't carry refreshed entitlements, so when their derived
- # fields change in another part of the company we sync them here to match
- # server behavior (see schematic-python merge.partial_company):
- # - credit_remaining <- credit_balances[credit_id]
- # - usage <- metric value matching (event_name, metric_period, month_reset)
- # Both are skipped when the partial also sends entitlements wholesale.
- def partial_company(existing, partial_data)
- return existing unless partial_data.is_a?(Hash)
-
- result = deep_copy(existing)
- entitlements_in_partial = partial_data.key?(:entitlements) || partial_data.key?("entitlements")
- updated_balances = nil
- metrics_updated = false
-
- partial_data.each do |key, value|
- sym_key = key.to_sym
- if COMPANY_MAP_FIELDS.include?(sym_key)
- result[sym_key] ||= {}
- result[sym_key] = result[sym_key].merge(value) if value.is_a?(Hash)
- updated_balances = (value.is_a?(Hash) ? value : {}) if sym_key == :credit_balances
- elsif COMPANY_ARRAY_FIELDS.include?(sym_key)
- result[sym_key] = value if value.is_a?(Array)
- elsif sym_key == :metrics
- result[sym_key] = upsert_metrics(result[sym_key] || [], value || [])
- metrics_updated = true
- else
- result[sym_key] = value
- end
- end
-
- if (updated_balances || metrics_updated) && !entitlements_in_partial
- result[:entitlements] = sync_entitlements(
- result[:entitlements], result[:metrics], updated_balances, metrics_updated
- )
- end
-
- result
- end
-
- USER_MAP_FIELDS = %i[keys traits].freeze
-
- def partial_user(existing, partial_data)
- return existing unless partial_data.is_a?(Hash)
-
- result = deep_copy(existing)
-
- partial_data.each do |key, value|
- sym_key = key.to_sym
- if USER_MAP_FIELDS.include?(sym_key)
- result[sym_key] ||= {}
- result[sym_key] = result[sym_key].merge(value) if value.is_a?(Hash)
- elsif sym_key == :rules
- result[sym_key] = value if value.is_a?(Array)
- else
- result[sym_key] = value
- end
- end
-
- result
- end
-
- def upsert_metrics(existing, incoming)
- return incoming if existing.nil? || existing.empty?
- return existing if incoming.nil? || incoming.empty?
-
- result = existing.map { |metric| deep_copy(metric) }
-
- incoming.each do |inc_metric|
- match_idx = result.index do |ex|
- metrics_match?(ex, inc_metric)
- end
-
- if match_idx
- result[match_idx] = inc_metric
- else
- result << inc_metric
- end
- end
-
- result
- end
-
- def metrics_match?(left, right)
- get_metric_field(left, :event_subtype) == get_metric_field(right, :event_subtype) &&
- get_metric_field(left, :period) == get_metric_field(right, :period) &&
- get_metric_field(left, :month_reset) == get_metric_field(right, :month_reset)
- end
-
- def get_metric_field(metric, field)
- metric[field] || metric[field.to_s]
- end
-
- # Re-derive entitlement usage / credit_remaining from the merged metrics
- # and the just-updated credit balances. Mirrors schematic-python so that
- # entitlement usage reflects DataStream track events immediately.
- def sync_entitlements(entitlements, metrics, updated_balances, metrics_updated)
- return entitlements unless entitlements.is_a?(Array) && !entitlements.empty?
-
- metrics_lookup = {}
- if metrics_updated && metrics.is_a?(Array)
- metrics.each do |metric|
- next unless metric.is_a?(Hash)
-
- key = [
- get_metric_field(metric, :event_subtype) || "",
- get_metric_field(metric, :period) || "",
- get_metric_field(metric, :month_reset) || ""
- ]
- value = get_metric_field(metric, :value)
- metrics_lookup[key] = value.nil? ? 0 : value
- end
- end
-
- entitlements.map do |ent|
- next ent unless ent.is_a?(Hash)
-
- new_ent = deep_copy(ent)
-
- credit_id = get_metric_field(ent, :credit_id)
- if updated_balances && credit_id
- present, balance = fetch_balance(updated_balances, credit_id)
- new_ent[:credit_remaining] = balance if present
- end
-
- event_name = get_metric_field(ent, :event_name)
- unless metrics_lookup.empty? || event_name.nil?
- period = get_metric_field(ent, :metric_period) || "all_time"
- month_reset = get_metric_field(ent, :month_reset) || "first_of_month"
- matched = metrics_lookup[[event_name, period, month_reset]]
- new_ent[:usage] = matched unless matched.nil?
- end
-
- new_ent
- end
- end
-
- # credit_balances keys may be symbols (deep_copy symbolizes) while an
- # entitlement's credit_id is always a string, so check both forms.
- def fetch_balance(balances, credit_id)
- return [true, balances[credit_id]] if balances.key?(credit_id)
- return [true, balances[credit_id.to_sym]] if balances.key?(credit_id.to_sym)
- return [true, balances[credit_id.to_s]] if balances.key?(credit_id.to_s)
-
- [false, nil]
- end
- end
- end
- end
- status: unresolved
- id: patch-46462128
- content_hash: sha256:94f06e4e687a0908d3f305a4d605d9dc1271db6ed83014a10b86be900187dd83
+ content_hash: sha256:416a511ede0736c55e936299b78f07a3935feedab63c1270dbb2230687fc6be4
original_commit: 46462128cacb25b73fac7770060cf427ff0d9e6d
original_message: add tests and address nits
original_author: Christopher Brady
- base_generation: e75de47f4e4f08f8e683366dd9b3936bc5c6452f
+ base_generation: 7ff89cac80a7358534185779ed4992b755919d97
files:
- lib/schematic/datastream/merge.rb
patch_content: |
diff --git a/lib/schematic/datastream/merge.rb b/lib/schematic/datastream/merge.rb
- index 7f1084d..f4dd4e5 100644
+ index f4dd4e5..5c32683 100644
--- a/lib/schematic/datastream/merge.rb
+++ b/lib/schematic/datastream/merge.rb
@@ -46,7 +46,7 @@ module Schematic
end
end
- - if (updated_balances || metrics_updated) && !entitlements_in_partial
- + if (updated_balances&.any? || metrics_updated) && !entitlements_in_partial
+ - if (updated_balances&.any? || metrics_updated) && !entitlements_in_partial
+ + if (updated_balances || metrics_updated) && !entitlements_in_partial
result[:entitlements] = sync_entitlements(
result[:entitlements], result[:metrics], updated_balances, metrics_updated
)
- @@ -152,12 +152,12 @@ module Schematic
+ @@ -152,9 +152,8 @@ module Schematic
end
end
- - # credit_balances keys may be symbols (deep_copy symbolizes) while an
- - # entitlement's credit_id is always a string, so check both forms.
- + # The partial's credit_balances may be keyed by string or symbol depending
- + # on how the message was parsed, while an entitlement's credit_id is a
- + # string value, so check both key forms.
+ - # The partial's credit_balances may be keyed by string or symbol depending
+ - # on how the message was parsed, while an entitlement's credit_id is a
+ - # string value, so check both key forms.
+ + # credit_balances keys may be symbols (deep_copy symbolizes) while an
+ + # entitlement's credit_id is always a string, so check both forms.
def fetch_balance(balances, credit_id)
return [true, balances[credit_id]] if balances.key?(credit_id)
return [true, balances[credit_id.to_sym]] if balances.key?(credit_id.to_sym)
- - return [true, balances[credit_id.to_s]] if balances.key?(credit_id.to_s)
-
- [false, nil]
- end
theirs_snapshot:
lib/schematic/datastream/merge.rb: |
# frozen_string_literal: true
@@ -400,7 +110,7 @@ patches:
end
end
- if (updated_balances&.any? || metrics_updated) && !entitlements_in_partial
+ if (updated_balances || metrics_updated) && !entitlements_in_partial
result[:entitlements] = sync_entitlements(
result[:entitlements], result[:metrics], updated_balances, metrics_updated
)
@@ -506,9 +216,8 @@ patches:
end
end
- # The partial's credit_balances may be keyed by string or symbol depending
- # on how the message was parsed, while an entitlement's credit_id is a
- # string value, so check both key forms.
+ # credit_balances keys may be symbols (deep_copy symbolizes) while an
+ # entitlement's credit_id is always a string, so check both forms.
def fetch_balance(balances, credit_id)
return [true, balances[credit_id]] if balances.key?(credit_id)
return [true, balances[credit_id.to_sym]] if balances.key?(credit_id.to_sym)
@@ -518,4 +227,3 @@ patches:
end
end
end
- status: unresolved
diff --git a/lib/schematic/datastream/merge.rb b/lib/schematic/datastream/merge.rb
index f4dd4e5..5c32683 100644
--- a/lib/schematic/datastream/merge.rb
+++ b/lib/schematic/datastream/merge.rb
@@ -46,7 +46,7 @@ def partial_company(existing, partial_data)
end
end
- if (updated_balances&.any? || metrics_updated) && !entitlements_in_partial
+ if (updated_balances || metrics_updated) && !entitlements_in_partial
result[:entitlements] = sync_entitlements(
result[:entitlements], result[:metrics], updated_balances, metrics_updated
)
@@ -152,9 +152,8 @@ def sync_entitlements(entitlements, metrics, updated_balances, metrics_updated)
end
end
- # The partial's credit_balances may be keyed by string or symbol depending
- # on how the message was parsed, while an entitlement's credit_id is a
- # string value, so check both key forms.
+ # credit_balances keys may be symbols (deep_copy symbolizes) while an
+ # entitlement's credit_id is always a string, so check both forms.
def fetch_balance(balances, credit_id)
return [true, balances[credit_id]] if balances.key?(credit_id)
return [true, balances[credit_id.to_sym]] if balances.key?(credit_id.to_sym)