from typing import Optional from rest_framework import serializers from django.utils.translation import gettext_lazy as _ from labs.models import LabFeedback, User, UserServerlessCluster from labs.lab_manager_v2.consts import supported_cloud_providers, supported_regions from labs.user_events import UserEvent, log_user_event from labs.lab_manager_v2.lab_spec import ( load_lab_spec, LabSpec, LabNotFoundError, TiDBEnvironmentType, ) class CreateLabSerializer(serializers.Serializer): lab_code = serializers.CharField(required=True) tidb_version = serializers.CharField(required=False) tidb_environment = serializers.CharField(required=True) region_name = serializers.CharField(required=True) cloud_provider = serializers.CharField(required=True) user_serverless_cluster_id = serializers.CharField(required=False) lab_spec: Optional[LabSpec] = None user_serverless_cluster: Optional[UserServerlessCluster] = None def validate_lab_code(self, value: str) -> str: try: self.lab_spec = load_lab_spec(value) except LabNotFoundError: raise serializers.ValidationError(_("Invalid lab code: %s") % value) if self.lab_spec.employee_only: user: User = self.context["request"].user if not (user.is_authenticated and user.is_pingcap_employee): raise serializers.ValidationError(_("This lab is for employees only")) return value def validate_tidb_environment(self, value: str) -> str: if value: try: return TiDBEnvironmentType(value) except ValueError: raise serializers.ValidationError( _("Invalid TiDB environment: %s") % value ) def validate_user_serverless_cluster_id(self, value: str) -> str: if value: try: self.user_serverless_cluster = UserServerlessCluster.objects.get( id=value, user=self.context["request"].user, ) except UserServerlessCluster.DoesNotExist: raise serializers.ValidationError( _("The specified TiDB Cloud Serverless cluster does not exist.") ) return value def validate(self, attrs: dict) -> dict: region_name = attrs["region_name"] cloud_provider = attrs["cloud_provider"] tidb_environment = attrs["tidb_environment"] tidb_version = attrs.get("tidb_version") if tidb_environment not in self.lab_spec.tidb_environments: raise serializers.ValidationError( { "tidb_environment": _( "This lab does not support the specified TiDB environment" ) } ) if tidb_environment == TiDBEnvironmentType.TIDB_SELF_MANAGED: if self.lab_spec.tidb_versions: if tidb_version: if tidb_version not in self.lab_spec.tidb_versions: raise serializers.ValidationError( { "tidb_version": _( "This lab does not support the specified TiDB version" ) } ) else: raise serializers.ValidationError( { "tidb_version": _( "TiDB version is required for self-managed labs" ) } ) else: if tidb_version: raise serializers.ValidationError( { "tidb_version": _( "This lab does not support specifying the tidb_version" ) } ) if cloud_provider not in self.lab_spec.regions.get_supported_cloud_providers(): raise serializers.ValidationError( {"cloud_provider": _("Invalid cloud provider: %s") % cloud_provider} ) if region_name not in self.lab_spec.regions.get_supported_regions( cloud_provider ): raise serializers.ValidationError( {"region_name": _("Invalid region name: %s") % region_name} ) if ( self.lab_spec.features.get("disable_serverless_sandbox", False) and not self.user_serverless_cluster ): raise serializers.ValidationError( { "user_serverless_cluster_id": _( "User serverless cluster ID is required since serverless sandbox cluster is disabled for this lab" ) } ) return attrs class LabGuidePartQuerySerializer(serializers.Serializer): language_code = serializers.CharField(required=False, default="en") class RedeemEventSerializer(serializers.Serializer): event_voucher_code = serializers.CharField(required=True) class LabFeedbackSerializer(serializers.ModelSerializer): class Meta: model = LabFeedback fields = [ "id", "lab_code", "rating_score", "comment", "created_at", "updated_at", ] class SubmitLabFeedbackSerializer(serializers.ModelSerializer): class Meta: model = LabFeedback fields = ["rating_score", "comment"] extra_kwargs = { "rating_score": {"required": True, "min_value": 1, "max_value": 5}, } def create(self, validated_data): email = self.context["request"].user.email lab_code = self.context["view"].kwargs["lab_code"] feedback, created = LabFeedback.objects.update_or_create( email=email, lab_code=lab_code, defaults=validated_data ) log_user_event( user=self.context["request"].user, event_name=UserEvent.EventName.SUBMIT_LAB_FEEDBACK, lab_code=lab_code, event_body={ "rating_score": validated_data["rating_score"], "comment": validated_data.get("comment", "")[:200], }, ) return feedback class BulkGenerateCreditsVoucherSerializer(serializers.Serializer): lark_user_union_id = serializers.CharField(required=False) redeemable_days = serializers.IntegerField(required=True) campaign_code = serializers.CharField(required=True, max_length=30, min_length=2) credits_per_voucher = serializers.IntegerField(required=True) voucher_count = serializers.IntegerField(required=True) voucher_category = serializers.CharField(required=True) customer_name = serializers.CharField(required=True) class TiDBCloudCreateLabServerlessClusterSerializer(serializers.Serializer): region = serializers.CharField(required=False) cloud_provider = serializers.CharField(required=False) cluster_id = serializers.IntegerField(required=False) def validate(self, attrs: dict) -> dict: cloud_provider = attrs.get("cloud_provider") region = attrs.get("region") cluster_id = attrs.get("cluster_id") if cluster_id is None: if not (region and cloud_provider): raise serializers.ValidationError( "region and cloud_provider must be provided when cluster_id is not provided" ) if cloud_provider not in supported_cloud_providers(): raise serializers.ValidationError( f"Unsupported cloud provider: {cloud_provider}" ) if region not in supported_regions(cloud_provider): raise serializers.ValidationError( f"Unsupported region: {region} for cloud provider: {cloud_provider}" ) return attrs