Skip to content

Commit

Permalink
change subscription filters to arrayfield (#707)
Browse files Browse the repository at this point in the history
  • Loading branch information
diego-escobedo authored Mar 19, 2023
1 parent e4c2539 commit ccefe73
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 205 deletions.
92 changes: 21 additions & 71 deletions backend/api/serializers/model_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
from django.conf import settings
from django.db.models import Max, Min, Sum
from drf_spectacular.utils import extend_schema_serializer
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from metering_billing.invoice import generate_balance_adjustment_invoice
from metering_billing.models import (
AddOnSpecification,
Expand Down Expand Up @@ -56,7 +53,6 @@
)
from metering_billing.utils import convert_to_date, now_utc
from metering_billing.utils.enums import (
CATEGORICAL_FILTER_OPERATORS,
CUSTOMER_BALANCE_ADJUSTMENT_STATUS,
FLAT_FEE_BEHAVIOR,
INVOICE_STATUS_ENUM,
Expand All @@ -70,6 +66,8 @@
USAGE_BEHAVIOR,
USAGE_BILLING_BEHAVIOR,
)
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

SVIX_CONNECTOR = settings.SVIX_CONNECTOR
logger = logging.getLogger("django.server")
Expand Down Expand Up @@ -199,36 +197,18 @@ class Meta:
comparison_value = serializers.ListField(child=serializers.CharField())


class SubscriptionCategoricalFilterSerializer(
ConvertEmptyStringToNullMixin, TimezoneFieldMixin, serializers.ModelSerializer
class SubscriptionFilterSerializer(
ConvertEmptyStringToNullMixin, TimezoneFieldMixin, serializers.Serializer
):
class Meta:
model = CategoricalFilter
fields = ("value", "property_name")
extra_kwargs = {
"property_name": {
"required": True,
},
"value": {"required": True},
}

value = serializers.CharField()
property_name = serializers.CharField(
help_text="The string name of the property to filter on. Example: 'product_id'"
)

def create(self, validated_data):
comparison_value = validated_data.pop("value")
comparison_value = [comparison_value]
validated_data["comparison_value"] = comparison_value
return CategoricalFilter.objects.get_or_create(
**validated_data, operator=CATEGORICAL_FILTER_OPERATORS.ISIN
)

def to_representation(self, instance):
data = {
"property_name": instance.property_name,
"value": instance.comparison_value[0],
"property_name": instance[0],
"value": instance[1],
}
return data

Expand Down Expand Up @@ -345,9 +325,7 @@ class Meta:
}

subscription_id = SubscriptionUUIDField(source="subscription_record_id")
subscription_filters = SubscriptionCategoricalFilterSerializer(
many=True, source="filters"
)
subscription_filters = SubscriptionFilterSerializer(many=True)
customer = LightweightCustomerSerializer()
billing_plan = LightweightPlanVersionSerializer()
addons = LightweightAddOnSubscriptionRecordSerializer(
Expand Down Expand Up @@ -445,11 +423,11 @@ def get_adjustments(self, obj) -> InvoiceLineItemAdjustmentSerializer(many=True)

def get_subscription_filters(
self, obj
) -> SubscriptionCategoricalFilterSerializer(many=True, allow_null=True):
) -> SubscriptionFilterSerializer(many=True, allow_null=True):
ass_sub_record = obj.associated_subscription_record
if ass_sub_record:
return SubscriptionCategoricalFilterSerializer(
ass_sub_record.filters.all(), many=True
return SubscriptionFilterSerializer(
ass_sub_record.subscription_filters, many=True
).data
return None

Expand Down Expand Up @@ -1640,7 +1618,7 @@ class Meta:
help_text="Whether the subscription automatically renews. Defaults to true.",
)
is_new = serializers.BooleanField(required=False)
subscription_filters = SubscriptionCategoricalFilterSerializer(
subscription_filters = SubscriptionFilterSerializer(
many=True,
required=False,
help_text="Add filter key, value pairs that define which events will be applied to this plan subscription.",
Expand Down Expand Up @@ -1683,22 +1661,9 @@ def create(self, validated_data):
filters = validated_data.pop("subscription_filters", [])
subscription_filters = []
for filter_data in filters:
sub_cat_filter_dict = {
"organization": validated_data["customer"].organization,
"property_name": filter_data["property_name"],
"operator": CATEGORICAL_FILTER_OPERATORS.ISIN,
"comparison_value": [filter_data["value"]],
}
try:
cf, _ = CategoricalFilter.objects.get_or_create(**sub_cat_filter_dict)
except CategoricalFilter.MultipleObjectsReturned:
cf = (
CategoricalFilter.objects.filter(**sub_cat_filter_dict)
.first()
.delete()
)
cf = CategoricalFilter.objects.filter(**sub_cat_filter_dict).first()
subscription_filters.append(cf)
subscription_filters.append(
[filter_data["property_name"], filter_data["value"]]
)
sr = SubscriptionRecord.create_subscription_record(
start_date=validated_data["start_date"],
end_date=validated_data.get("end_date"),
Expand Down Expand Up @@ -1759,7 +1724,7 @@ class Meta:
help_text="Whether the subscription automatically renews. Defaults to true.",
)
is_new = serializers.BooleanField(required=False)
subscription_filters = SubscriptionCategoricalFilterSerializer(
subscription_filters = SubscriptionFilterSerializer(
many=True,
required=False,
help_text="Add filter key, value pairs that define which events will be applied to this plan subscription.",
Expand Down Expand Up @@ -1825,22 +1790,9 @@ def create(self, validated_data):
filters = validated_data.pop("subscription_filters", [])
subscription_filters = []
for filter_data in filters:
sub_cat_filter_dict = {
"organization": validated_data["customer"].organization,
"property_name": filter_data["property_name"],
"operator": CATEGORICAL_FILTER_OPERATORS.ISIN,
"comparison_value": [filter_data["value"]],
}
try:
cf, _ = CategoricalFilter.objects.get_or_create(**sub_cat_filter_dict)
except CategoricalFilter.MultipleObjectsReturned:
cf = (
CategoricalFilter.objects.filter(**sub_cat_filter_dict)
.first()
.delete()
)
cf = CategoricalFilter.objects.filter(**sub_cat_filter_dict).first()
subscription_filters.append(cf)
subscription_filters.append(
[filter_data["property_name"], filter_data["value"]]
)
sr = SubscriptionRecord.create_subscription_record(
start_date=validated_data["start_date"],
end_date=validated_data.get("end_date"),
Expand Down Expand Up @@ -1869,9 +1821,7 @@ class Meta(SubscriptionRecordSerializer.Meta):
plan_detail = LightweightPlanVersionSerializer(
source="billing_plan", read_only=True
)
subscription_filters = SubscriptionCategoricalFilterSerializer(
source="filters", many=True, read_only=True
)
subscription_filters = SubscriptionFilterSerializer(many=True, read_only=True)


class SubscriptionInvoiceSerializer(SubscriptionRecordSerializer):
Expand Down Expand Up @@ -2137,7 +2087,7 @@ class SubscriptionRecordFilterSerializer(serializers.Serializer):
required=True,
help_text="Filter to a specific plan.",
)
subscription_filters = SubscriptionCategoricalFilterSerializer(
subscription_filters = SubscriptionFilterSerializer(
many=True,
required=False,
help_text="Filter to a specific set of subscription filters. If your billing model only allows for one subscription per customer, you very likely do not need this field. Must be formatted as a JSON-encoded + stringified list of dictionaries, where each dictionary has a key of 'property_name' and a key of 'value'.",
Expand Down Expand Up @@ -2222,7 +2172,7 @@ class AddOnSubscriptionRecordFilterSerializer(serializers.Serializer):
required=True,
help_text="Filter to a specific plan.",
)
attached_subscription_filters = SubscriptionCategoricalFilterSerializer(
attached_subscription_filters = SubscriptionFilterSerializer(
many=True,
required=False,
help_text="Filter to a specific set of subscription filters. If your billing model only allows for one subscription per customer, you very likely do not need this field. Must be formatted as a JSON-encoded + stringified list of dictionaries, where each dictionary has a key of 'property_name' and a key of 'value'.",
Expand Down
10 changes: 4 additions & 6 deletions backend/api/serializers/nonmodel_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
LightweightCustomerSerializer,
LightweightMetricSerializer,
LightweightPlanVersionSerializer,
SubscriptionCategoricalFilterSerializer,
SubscriptionFilterSerializer,
)
from metering_billing.models import (
Customer,
Expand Down Expand Up @@ -70,9 +70,7 @@ class Meta:
"plan": {"required": True, "read_only": True},
}

subscription_filters = SubscriptionCategoricalFilterSerializer(
many=True, source="filters"
)
subscription_filters = SubscriptionFilterSerializer(many=True)
plan = LightweightPlanVersionSerializer(source="billing_plan")


Expand Down Expand Up @@ -120,7 +118,7 @@ class MetricAccessRequestSerializer(serializers.Serializer):
queryset=Metric.objects.all(),
help_text="The metric_id of the metric you want to check access for.",
)
subscription_filters = SubscriptionCategoricalFilterSerializer(
subscription_filters = SubscriptionFilterSerializer(
many=True,
required=False,
help_text="Used if you want to restrict the access check to only plans that fulfill certain subscription filter criteria. If your billing model does not have the ability multiple plans or subscriptions per customer, this is likely not relevant for you. ",
Expand Down Expand Up @@ -158,7 +156,7 @@ class FeatureAccessRequestSerializer(serializers.Serializer):
queryset=Feature.objects.all(),
help_text="The feature_id of the feature you want to check access for.",
)
subscription_filters = SubscriptionCategoricalFilterSerializer(
subscription_filters = SubscriptionFilterSerializer(
many=True,
required=False,
help_text="The subscription filters that are applied to this plan's relationship with the customer. If your billing model does not have the ability multiple plans or subscriptions per customer, this is likely not relevant for you. ",
Expand Down
Loading

0 comments on commit ccefe73

Please sign in to comment.