Skip to content

Commit

Permalink
change feature lookup parameters (#485)
Browse files Browse the repository at this point in the history
  • Loading branch information
diego-escobedo committed Jan 21, 2023
1 parent 158035f commit 772b6d5
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 35 deletions.
13 changes: 11 additions & 2 deletions backend/api/serializers/model_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from metering_billing.payment_providers import PAYMENT_PROVIDER_MAP
from metering_billing.serializers.serializer_utils import (
BalanceAdjustmentUUIDField,
FeatureUUIDField,
InvoiceUUIDField,
MetricUUIDField,
PlanUUIDField,
Expand Down Expand Up @@ -741,14 +742,21 @@ class FeatureSerializer(
class Meta:
model = Feature
fields = (
"feature_id",
"feature_name",
"feature_description",
)
extra_kwargs = {
"feature_name": {"required": True},
"feature_description": {"required": True},
"feature_id": {
"required": True,
"read_only": True,
},
"feature_name": {"required": True, "read_only": True},
"feature_description": {"required": True, "read_only": True},
}

feature_id = FeatureUUIDField()


class PriceTierSerializer(
ConvertEmptyStringToSerializerMixin, serializers.ModelSerializer
Expand Down Expand Up @@ -1635,3 +1643,4 @@ class Meta:
metric = MetricSerializer()
plan_version = LightweightPlanVersionSerializer()
plan_version = LightweightPlanVersionSerializer()
plan_version = LightweightPlanVersionSerializer()
35 changes: 24 additions & 11 deletions backend/metering_billing/serializers/model_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,12 @@ class Meta:
price_adjustment_name = serializers.CharField(default="")


class FeatureCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Feature
fields = ("feature_name", "feature_description")


class PlanVersionCreateSerializer(serializers.ModelSerializer):
class Meta:
model = PlanVersion
Expand Down Expand Up @@ -1038,7 +1044,13 @@ class Meta:
components = PlanComponentCreateSerializer(
many=True, allow_null=True, required=False, source="plan_components"
)
features = FeatureSerializer(many=True, allow_null=True, required=False)
features = SlugRelatedFieldWithOrganization(
slug_field="feature_id",
queryset=Feature.objects.all(),
many=True,
allow_null=True,
required=False,
)
price_adjustment = PriceAdjustmentSerializer(required=False)
plan_id = SlugRelatedFieldWithOrganization(
slug_field="plan_id",
Expand Down Expand Up @@ -1135,16 +1147,17 @@ def create(self, validated_data):
for component in components:
component.plan_version = billing_plan
component.save()
for feature_data in features_data:
feature_data["organization"] = org
description = feature_data.pop("description", None)
try:
f, created = Feature.objects.get_or_create(**feature_data)
if created and description:
f.description = description
f.save()
except Feature.MultipleObjectsReturned:
f = Feature.objects.filter(**feature_data).first()
for f in features_data:
# feature_data["organization"] = org
# description = feature_data.pop("description", None)
# try:
# f, created = Feature.objects.get_or_create(**feature_data)
# if created and description:
# f.description = description
# f.save()
# except Feature.MultipleObjectsReturned:
# f = Feature.objects.filter(**feature_data).first()
assert type(f) is Feature
billing_plan.features.add(f)
if price_adjustment_data:
price_adjustment_data["organization"] = org
Expand Down
12 changes: 12 additions & 0 deletions backend/metering_billing/serializers/serializer_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def get_queryset(self):
def to_internal_value(self, data):
from metering_billing.models import (
CustomerBalanceAdjustment,
Feature,
Metric,
Plan,
PlanVersion,
Expand All @@ -28,11 +29,14 @@ def to_internal_value(self, data):
data = PlanUUIDField().to_internal_value(data)
elif self.queryset.model is PlanVersion:
data = PlanVersionUUIDField().to_internal_value(data)
elif self.queryset.model is Feature:
data = FeatureUUIDField().to_internal_value(data)
return super().to_internal_value(data)

def to_representation(self, obj):
from metering_billing.models import (
CustomerBalanceAdjustment,
Feature,
Metric,
Plan,
PlanVersion,
Expand All @@ -47,6 +51,8 @@ def to_representation(self, obj):
return PlanUUIDField().to_representation(obj.plan_id)
elif isinstance(obj, PlanVersion):
return PlanVersionUUIDField().to_representation(obj.version_id)
elif isinstance(obj, Feature):
return FeatureUUIDField().to_representation(obj.feature_id)
return repr


Expand Down Expand Up @@ -144,6 +150,12 @@ def __init__(self, *args, **kwargs):
super().__init__("plan_version_", *args, **kwargs)


@extend_schema_field(serializers.RegexField(regex=r"feature_[0-9a-f]{32}"))
class FeatureUUIDField(UUIDPrefixField):
def __init__(self, *args, **kwargs):
super().__init__("feature_", *args, **kwargs)


@extend_schema_field(serializers.RegexField(regex=r"sub_[0-9a-f]{32}"))
class SubscriptionUUIDField(UUIDPrefixField):
def __init__(self, *args, **kwargs):
Expand Down
20 changes: 18 additions & 2 deletions backend/metering_billing/views/model_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
CustomerUpdateSerializer,
EventSerializer,
ExternalPlanLinkSerializer,
FeatureCreateSerializer,
FeatureSerializer,
MetricCreateSerializer,
MetricSerializer,
Expand Down Expand Up @@ -527,9 +528,15 @@ class FeatureViewSet(
}
queryset = Feature.objects.all()

def get_serializer_class(self):
if self.action == "create":
return FeatureCreateSerializer
return FeatureSerializer

def get_queryset(self):
organization = self.request.organization
return Feature.objects.filter(organization=organization)
objs = Feature.objects.filter(organization=organization)
return objs

def get_serializer_context(self):
context = super().get_serializer_context()
Expand Down Expand Up @@ -558,8 +565,16 @@ def dispatch(self, request, *args, **kwargs):
)
return response

@extend_schema(responses=FeatureSerializer)
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
instance = self.perform_create(serializer)
feature_data = FeatureSerializer(instance).data
return Response(feature_data, status=status.HTTP_201_CREATED)

def perform_create(self, serializer):
serializer.save(organization=self.request.organization)
return serializer.save(organization=self.request.organization)


class PlanVersionViewSet(PermissionPolicyMixin, viewsets.ModelViewSet):
Expand Down Expand Up @@ -1131,3 +1146,4 @@ def get_serializer_context(self):
organization = self.request.organization
context.update({"organization": organization})
return context
return context
4 changes: 2 additions & 2 deletions frontend/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import {
OrganizationType,
PaginatedActionsType,
} from "../types/account-type";
import { FeatureType } from "../types/feature-type";
import { FeatureType, CreateFeatureType } from "../types/feature-type";
import Cookies from "universal-cookie";
import {
CreateBacktestType,
Expand Down Expand Up @@ -408,7 +408,7 @@ export const PlansByCustomer = {

export const Features = {
getFeatures: (): Promise<FeatureType[]> => requests.get("app/features/"),
createFeature: (post: FeatureType): Promise<FeatureType> =>
createFeature: (post: CreateFeatureType): Promise<FeatureType> =>
requests.post("app/features/", post),
};

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Plans/FeatureDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const FeatureDisplay: FC<{
type="text"
icon={<DeleteOutlined />}
danger
onClick={() => removeFeature(feature.feature_name)}
onClick={() => removeFeature(feature.feature_id)}
/>
</div>
<div className="planFeatureDesc px-4">
Expand Down
13 changes: 9 additions & 4 deletions frontend/src/components/Plans/FeatureForm.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useState } from "react";
import { FeatureType } from "../../types/feature-type";
import { FeatureType, CreateFeatureType } from "../../types/feature-type";
import { Button, Divider, Modal, Select, Input, message } from "antd";
import { Features } from "../../api/api";
import { UseQueryResult, useQuery } from "react-query";
import { useMutation, useQueryClient } from "react-query";

const { Option } = Select;

Expand All @@ -15,7 +16,7 @@ const FeatureForm = (props: {
const [createdFeatureName, setCreatedFeatureName] = useState<string>("");
const [createdFeatureDescription, setCreatedFeatureDescription] =
useState<string>("");

const queryClient = useQueryClient();
const {
data: features,
isLoading,
Expand Down Expand Up @@ -49,11 +50,15 @@ const FeatureForm = (props: {
if (featureExists) {
message.error("Feature already exists");
} else {
const newFeature: FeatureType = {
const newFeature: CreateFeatureType = {
feature_name: createdFeatureName,
feature_description: createdFeatureDescription,
};
setNewFeatures([...newFeatures, newFeature]);

Features.createFeature(newFeature).then((res) => {
queryClient.invalidateQueries("feature_list");
setNewFeatures([...newFeatures, res]);
});
setCreatedFeatureName("");
setCreatedFeatureDescription("");
}
Expand Down
22 changes: 15 additions & 7 deletions frontend/src/pages/CreatePlan.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
PlanType,
} from "../types/plan-type";
import { Plan, Organization } from "../api/api";
import { FeatureType } from "../types/feature-type";
import { CreateFeatureType, FeatureType } from "../types/feature-type";
import FeatureForm from "../components/Plans/FeatureForm";
import LinkExternalIds from "../components/Plans/LinkExternalIds";
import { PageLayout } from "../components/base/PageLayout";
Expand All @@ -49,8 +49,8 @@ const durationConversion = {
const CreatePlan = () => {
const [componentVisible, setcomponentVisible] = useState<boolean>();
const [allPlans, setAllPlans] = useState<PlanType[]>([]);
const [allCurrencies, setAllCurrencies] = useState<PricingUnit[]>([]);
const [selectedCurrency, setSelectedCurrency] = useState<PricingUnit>({
const [allCurrencies, setAllCurrencies] = useState<CurrencyType[]>([]);
const [selectedCurrency, setSelectedCurrency] = useState<CurrencyType>({
symbol: "",
code: "",
name: "",
Expand Down Expand Up @@ -112,7 +112,7 @@ const CreatePlan = () => {
for (let i = 0; i < newFeatures.length; i++) {
if (
planFeatures.some(
(feat) => feat.feature_name === newFeatures[i].feature_name
(feat) => feat.feature_id === newFeatures[i].feature_id
)
) {
} else {
Expand All @@ -129,9 +129,9 @@ const CreatePlan = () => {
setFeatureVisible(true);
};

const removeFeature = (feature_name: string) => {
const removeFeature = (feature_id: string) => {
setPlanFeatures(
planFeatures.filter((item) => item.feature_name !== feature_name)
planFeatures.filter((item) => item.feature_id !== feature_id)
);
};

Expand Down Expand Up @@ -213,6 +213,14 @@ const CreatePlan = () => {
}
}

const featureIdList: string[] = [];
const features: any = Object.values(planFeatures);
if (features) {
for (let i = 0; i < features.length; i++) {
featureIdList.push(features[i].feature_id);
}
}

if (values.usage_billing_frequency === "yearly") {
values.usage_billing_frequency = "end_of_period";
}
Expand All @@ -222,7 +230,7 @@ const CreatePlan = () => {
transition_to_plan_id: values.transition_to_plan_id,
flat_rate: values.flat_rate,
components: usagecomponentslist,
features: planFeatures,
features: featureIdList,
usage_billing_frequency: values.usage_billing_frequency,
currency_code: values.plan_currency,
};
Expand Down
18 changes: 14 additions & 4 deletions frontend/src/pages/EditPlan.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const EditPlan = ({ type, plan, versionIndex }: Props) => {
useState<boolean>(false);
const [activeVersion, setActiveVersion] = useState<boolean>(false);
const [activeVersionType, setActiveVersionType] = useState<string>();
const [allCurrencies, setAllCurrencies] = useState<PricingUnit[]>([]);
const [allCurrencies, setAllCurrencies] = useState<CurrencyType[]>([]);
const navigate = useNavigate();
const [componentsData, setComponentsData] = useState<any>([]);
const [form] = Form.useForm();
Expand All @@ -80,7 +80,7 @@ const EditPlan = ({ type, plan, versionIndex }: Props) => {
plan.versions[versionIndex].price_adjustment?.price_adjustment_type ??
"none"
);
const [selectedCurrency, setSelectedCurrency] = useState<PricingUnit>(
const [selectedCurrency, setSelectedCurrency] = useState<CurrencyType>(
plan.versions[versionIndex].currency ?? {
symbol: "",
code: "",
Expand Down Expand Up @@ -301,6 +301,16 @@ const EditPlan = ({ type, plan, versionIndex }: Props) => {
usagecomponentslist.push(usagecomponent);
}
}

const featureIdList: string[] = [];
const features: any = Object.values(planFeatures);
if (features) {
for (let i = 0; i < features.length; i++) {
featureIdList.push(features[i].feature_id);
}
}


if (values.usage_billing_frequency === "yearly") {
values.usage_billing_frequency = "end_of_period";
}
Expand All @@ -311,7 +321,7 @@ const EditPlan = ({ type, plan, versionIndex }: Props) => {
transition_to_plan_id: values.transition_to_plan_id,
flat_rate: values.flat_rate,
components: usagecomponentslist,
features: planFeatures,
features: featureIdList,
usage_billing_frequency: values.usage_billing_frequency,
currency_code: values.plan_currency.code,
};
Expand Down Expand Up @@ -361,7 +371,7 @@ const EditPlan = ({ type, plan, versionIndex }: Props) => {
transition_to_plan_id: values.transition_to_plan_id,
flat_rate: values.flat_rate,
components: usagecomponentslist,
features: planFeatures,
features: featureIdList,
usage_billing_frequency: values.usage_billing_frequency,
make_active: activeVersion,
make_active_type: activeVersionType,
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/types/feature-type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
export interface FeatureType {
feature_id: string;
feature_name: string;
feature_description: string;
}

export interface CreateFeatureType {
feature_name: string;
feature_description: string;
}

0 comments on commit 772b6d5

Please sign in to comment.