Merge branch 'v3' of github.com:jumpserver/jumpserver into v3

pull/9044/head
ibuler 2022-11-09 20:59:28 +08:00
commit 644f3f1783
16 changed files with 93 additions and 107 deletions

View File

@ -2,7 +2,9 @@ from django.utils.translation import ugettext as _
from rest_framework import serializers from rest_framework import serializers
from common.drf.serializers import BulkModelSerializer from common.drf.serializers import BulkModelSerializer
from common.drf.serializers import MethodSerializer from common.drf.serializers import MethodSerializer
from common.drf.fields import ObjectRelatedField
from jumpserver.utils import has_valid_xpack_license from jumpserver.utils import has_valid_xpack_license
from users.models import User
from ..models import LoginACL from ..models import LoginACL
from .rules import RuleSerializer from .rules import RuleSerializer
@ -12,8 +14,10 @@ common_help_text = _('Format for comma-delimited string, with * indicating a mat
class LoginACLSerializer(BulkModelSerializer): class LoginACLSerializer(BulkModelSerializer):
user_display = serializers.ReadOnlyField(source='user.username', label=_('Username')) user = ObjectRelatedField(queryset=User.objects, label=_('User'))
reviewers_display = serializers.SerializerMethodField(label=_('Reviewers')) reviewers = ObjectRelatedField(
queryset=User.objects, label=_('Reviewers'), many=True, required=False
)
action_display = serializers.ReadOnlyField(source='get_action_display', label=_('Action')) action_display = serializers.ReadOnlyField(source='get_action_display', label=_('Action'))
reviewers_amount = serializers.IntegerField(read_only=True, source='reviewers.count') reviewers_amount = serializers.IntegerField(read_only=True, source='reviewers.count')
rules = MethodSerializer() rules = MethodSerializer()
@ -22,13 +26,11 @@ class LoginACLSerializer(BulkModelSerializer):
model = LoginACL model = LoginACL
fields_mini = ['id', 'name'] fields_mini = ['id', 'name']
fields_small = fields_mini + [ fields_small = fields_mini + [
'priority', 'rules', 'action', 'action_display', 'priority', 'rules', 'action', 'action_display', 'is_active', 'user',
'is_active', 'user', 'user_display', 'date_created', 'date_updated', 'reviewers_amount', 'comment', 'created_by',
'date_created', 'date_updated', 'reviewers_amount',
'comment', 'created_by'
] ]
fields_fk = ['user', 'user_display'] fields_fk = ['user']
fields_m2m = ['reviewers', 'reviewers_display'] fields_m2m = ['reviewers']
fields = fields_small + fields_fk + fields_m2m fields = fields_small + fields_fk + fields_m2m
extra_kwargs = { extra_kwargs = {
'priority': {'default': 50}, 'priority': {'default': 50},

View File

@ -111,8 +111,8 @@ class AutomationExecutionViewSet(
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
automation = serializer.validated_data.get('automation') automation = serializer.validated_data.get('automation')
tp = serializer.validated_data.get('type') tp = serializer.validated_data.get('type')
model = AutomationTypes.get_model(tp) model = AutomationTypes.get_type_model(tp)
task = execute_automation.delay( task = execute_automation.delay(
pid=automation.ok, trigger=Trigger.manual, model=model pid=automation.pk, trigger=Trigger.manual, model=model
) )
return Response({'task': task.id}, status=status.HTTP_201_CREATED) return Response({'task': task.id}, status=status.HTTP_201_CREATED)

View File

@ -24,8 +24,8 @@ class ChangeSecretAutomationViewSet(OrgBulkModelViewSet):
class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet): class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
serializer_class = serializers.ChangeSecretRecordSerializer serializer_class = serializers.ChangeSecretRecordSerializer
filter_fields = ['username', 'asset', 'reason', 'execution'] filter_fields = ['asset', 'execution_id']
search_fields = ['username', 'reason', 'asset__hostname'] search_fields = ['asset__hostname']
def get_queryset(self): def get_queryset(self):
return ChangeSecretRecord.objects.all() return ChangeSecretRecord.objects.all()
@ -36,5 +36,5 @@ class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
execution = get_object_or_none(AutomationExecution, pk=eid) execution = get_object_or_none(AutomationExecution, pk=eid)
if execution: if execution:
queryset = queryset.filter(execution=execution) queryset = queryset.filter(execution=execution)
queryset = queryset.order_by('is_success', '-date_start') queryset = queryset.order_by('-date_start')
return queryset return queryset

View File

@ -232,5 +232,6 @@ class BasePlaybookManager:
except Exception as e: except Exception as e:
self.on_runner_failed(runner, e) self.on_runner_failed(runner, e)
print('\n') print('\n')
self.execution.status = 'success'
self.execution.date_finished = timezone.now() self.execution.date_finished = timezone.now()
self.execution.save() self.execution.save()

View File

@ -15,12 +15,8 @@ from assets.const import AutomationTypes
class BaseAutomation(CommonModelMixin, PeriodTaskModelMixin, OrgModelMixin): class BaseAutomation(CommonModelMixin, PeriodTaskModelMixin, OrgModelMixin):
accounts = models.JSONField(default=list, verbose_name=_("Accounts")) accounts = models.JSONField(default=list, verbose_name=_("Accounts"))
nodes = models.ManyToManyField( nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes"))
'assets.Node', blank=True, verbose_name=_("Nodes") assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets"))
)
assets = models.ManyToManyField(
'assets.Asset', blank=True, verbose_name=_("Assets")
)
type = models.CharField(max_length=16, choices=AutomationTypes.choices, verbose_name=_('Type')) type = models.CharField(max_length=16, choices=AutomationTypes.choices, verbose_name=_('Type'))
is_active = models.BooleanField(default=True, verbose_name=_("Is active")) is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
comment = models.TextField(blank=True, verbose_name=_('Comment')) comment = models.TextField(blank=True, verbose_name=_('Comment'))
@ -92,7 +88,7 @@ class AutomationExecution(OrgModelMixin):
'BaseAutomation', related_name='executions', on_delete=models.CASCADE, 'BaseAutomation', related_name='executions', on_delete=models.CASCADE,
verbose_name=_('Automation task') verbose_name=_('Automation task')
) )
status = models.CharField(max_length=16, default='pending') status = models.CharField(max_length=16, default='pending', verbose_name=_('Status'))
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created')) date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created'))
date_start = models.DateTimeField(null=True, verbose_name=_('Date start'), db_index=True) date_start = models.DateTimeField(null=True, verbose_name=_('Date start'), db_index=True)
date_finished = models.DateTimeField(null=True, verbose_name=_("Date finished")) date_finished = models.DateTimeField(null=True, verbose_name=_("Date finished"))

View File

@ -13,3 +13,7 @@ class GatherAccountsAutomation(BaseAutomation):
class Meta: class Meta:
verbose_name = _("Gather asset accounts") verbose_name = _("Gather asset accounts")
@property
def executed_amount(self):
return self.executions.count()

View File

@ -58,7 +58,6 @@ class AccountSerializer(AccountSerializerCreateMixin, BaseAccountSerializer):
required=False, queryset=Asset.objects, required=False, queryset=Asset.objects,
label=_('Asset'), attrs=('id', 'name', 'address', 'platform_id') label=_('Asset'), attrs=('id', 'name', 'address', 'platform_id')
) )
secret_type = LabeledChoiceField(choices=SecretType.choices, label=_('Secret type'))
class Meta(BaseAccountSerializer.Meta): class Meta(BaseAccountSerializer.Meta):
model = Account model = Account

View File

@ -6,6 +6,8 @@ from rest_framework import serializers
from orgs.mixins.serializers import BulkOrgResourceModelSerializer from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from ops.mixin import PeriodTaskSerializerMixin from ops.mixin import PeriodTaskSerializerMixin
from common.utils import get_logger from common.utils import get_logger
from common.const.choices import Trigger
from common.drf.fields import LabeledChoiceField
from assets.models import AccountBackupPlan, AccountBackupPlanExecution from assets.models import AccountBackupPlan, AccountBackupPlanExecution
@ -32,17 +34,12 @@ class AccountBackupPlanSerializer(PeriodTaskSerializerMixin, BulkOrgResourceMode
class AccountBackupPlanExecutionSerializer(serializers.ModelSerializer): class AccountBackupPlanExecutionSerializer(serializers.ModelSerializer):
trigger_display = serializers.ReadOnlyField( trigger = LabeledChoiceField(choices=Trigger.choices, label=_('Trigger mode'))
source='get_trigger_display', label=_('Trigger mode')
)
class Meta: class Meta:
model = AccountBackupPlanExecution model = AccountBackupPlanExecution
fields = [ read_only_fields = [
'id', 'date_start', 'timedelta', 'plan_snapshot', 'trigger', 'reason',
'is_success', 'plan', 'org_id', 'recipients', 'trigger_display'
]
read_only_fields = (
'id', 'date_start', 'timedelta', 'plan_snapshot', 'trigger', 'reason', 'id', 'date_start', 'timedelta', 'plan_snapshot', 'trigger', 'reason',
'is_success', 'org_id', 'recipients' 'is_success', 'org_id', 'recipients'
) ]
fields = read_only_fields + ['plan']

View File

@ -1,28 +1,19 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from io import StringIO
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from common.utils import validate_ssh_private_key, ssh_private_key_gen
from common.drf.fields import EncryptedField
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from assets.models import BaseAccount from assets.models import BaseAccount
from assets.serializers.base import AuthValidateMixin
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
__all__ = ['BaseAccountSerializer'] __all__ = ['BaseAccountSerializer']
class BaseAccountSerializer(BulkOrgResourceModelSerializer): class BaseAccountSerializer(AuthValidateMixin, BulkOrgResourceModelSerializer):
secret = EncryptedField(
label=_('Secret'), required=False, allow_blank=True,
allow_null=True, max_length=40960
)
class Meta: class Meta:
model = BaseAccount model = BaseAccount
fields_mini = ['id', 'name', 'username'] fields_mini = ['id', 'name', 'username']
fields_small = fields_mini + [ fields_small = fields_mini + [
'secret_type', 'secret', 'has_secret', 'secret_type', 'secret', 'has_secret', 'passphrase',
'privileged', 'is_active', 'specific', 'privileged', 'is_active', 'specific',
] ]
fields_other = ['created_by', 'date_created', 'date_updated', 'comment'] fields_other = ['created_by', 'date_created', 'date_updated', 'comment']
@ -32,29 +23,5 @@ class BaseAccountSerializer(BulkOrgResourceModelSerializer):
'date_verified', 'created_by', 'date_created', 'date_verified', 'created_by', 'date_created',
] ]
extra_kwargs = { extra_kwargs = {
'secret': {'write_only': True},
'passphrase': {'write_only': True},
'specific': {'label': _('Specific')}, 'specific': {'label': _('Specific')},
} }
def validate_private_key(self, private_key):
if not private_key:
return ''
passphrase = self.initial_data.get('passphrase')
passphrase = passphrase if passphrase else None
valid = validate_ssh_private_key(private_key, password=passphrase)
if not valid:
raise serializers.ValidationError(_("private key invalid or passphrase error"))
private_key = ssh_private_key_gen(private_key, password=passphrase)
string_io = StringIO()
private_key.write_private_key(string_io)
private_key = string_io.getvalue()
return private_key
def validate_secret(self, value):
secret_type = self.initial_data.get('secret_type')
if secret_type == 'ssh_key':
value = self.validate_private_key(value)
return value

View File

@ -3,9 +3,10 @@ from rest_framework import serializers
from ops.mixin import PeriodTaskSerializerMixin from ops.mixin import PeriodTaskSerializerMixin
from assets.const import AutomationTypes from assets.const import AutomationTypes
from assets.models import Asset, BaseAutomation, AutomationExecution from assets.models import Asset, Node, BaseAutomation, AutomationExecution
from orgs.mixins.serializers import BulkOrgResourceModelSerializer from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from common.utils import get_logger from common.utils import get_logger
from common.drf.fields import ObjectRelatedField
logger = get_logger(__file__) logger = get_logger(__file__)
@ -16,6 +17,9 @@ __all__ = [
class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSerializer): class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSerializer):
assets = ObjectRelatedField(many=True, required=False, queryset=Asset.objects, label=_('Assets'))
nodes = ObjectRelatedField(many=True, required=False, queryset=Node.objects, label=_('Nodes'))
class Meta: class Meta:
read_only_fields = [ read_only_fields = [
'date_created', 'date_updated', 'created_by', 'periodic_display' 'date_created', 'date_updated', 'created_by', 'periodic_display'
@ -26,6 +30,7 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe
] ]
extra_kwargs = { extra_kwargs = {
'name': {'required': True}, 'name': {'required': True},
'type': {'read_only': True},
'periodic_display': {'label': _('Periodic perform')}, 'periodic_display': {'label': _('Periodic perform')},
} }
@ -37,10 +42,10 @@ class AutomationExecutionSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = AutomationExecution model = AutomationExecution
fields = [ read_only_fields = [
'id', 'automation', 'trigger', 'trigger_display', 'trigger_display', 'date_start', 'date_finished', 'snapshot', 'status'
'date_start', 'date_finished', 'snapshot', 'type'
] ]
fields = ['id', 'automation', 'trigger', 'type'] + read_only_fields
@staticmethod @staticmethod
def get_snapshot(obj): def get_snapshot(obj):

View File

@ -3,10 +3,11 @@
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from rest_framework import serializers from rest_framework import serializers
from assets.serializers.base import AuthValidateMixin
from assets.models import ChangeSecretAutomation, ChangeSecretRecord
from assets.const import DEFAULT_PASSWORD_RULES, SecretType, SecretStrategy
from common.utils import get_logger from common.utils import get_logger
from common.drf.fields import LabeledChoiceField, ObjectRelatedField
from assets.serializers.base import AuthValidateMixin
from assets.const import DEFAULT_PASSWORD_RULES, SecretType, SecretStrategy, SSHKeyStrategy
from assets.models import Asset, Account, ChangeSecretAutomation, ChangeSecretRecord, AutomationExecution
from .base import BaseAutomationSerializer from .base import BaseAutomationSerializer
@ -20,19 +21,17 @@ __all__ = [
class ChangeSecretAutomationSerializer(AuthValidateMixin, BaseAutomationSerializer): class ChangeSecretAutomationSerializer(AuthValidateMixin, BaseAutomationSerializer):
secret_strategy = LabeledChoiceField(
choices=SecretStrategy.choices, required=True, label=_('Secret strategy')
)
ssh_key_change_strategy = LabeledChoiceField(
choices=SSHKeyStrategy.choices, required=False, label=_('SSH Key strategy')
)
password_rules = serializers.DictField(default=DEFAULT_PASSWORD_RULES) password_rules = serializers.DictField(default=DEFAULT_PASSWORD_RULES)
secret_strategy_display = serializers.ReadOnlyField(
source='get_secret_strategy_display', label=_('Secret strategy')
)
ssh_key_change_strategy_display = serializers.ReadOnlyField(
source='get_ssh_key_strategy_display', label=_('SSH Key strategy')
)
class Meta: class Meta:
model = ChangeSecretAutomation model = ChangeSecretAutomation
read_only_fields = BaseAutomationSerializer.Meta.read_only_fields + [ read_only_fields = BaseAutomationSerializer.Meta.read_only_fields
'secret_strategy_display', 'ssh_key_change_strategy_display'
]
fields = BaseAutomationSerializer.Meta.fields + read_only_fields + [ fields = BaseAutomationSerializer.Meta.fields + read_only_fields + [
'secret_type', 'secret_strategy', 'secret', 'password_rules', 'secret_type', 'secret_strategy', 'secret', 'password_rules',
'ssh_key_change_strategy', 'passphrase', 'recipients', 'ssh_key_change_strategy', 'passphrase', 'recipients',
@ -84,26 +83,21 @@ class ChangeSecretAutomationSerializer(AuthValidateMixin, BaseAutomationSerializ
class ChangeSecretRecordSerializer(serializers.ModelSerializer): class ChangeSecretRecordSerializer(serializers.ModelSerializer):
asset_display = serializers.SerializerMethodField(label=_('Asset display'))
account_display = serializers.SerializerMethodField(label=_('Account display'))
is_success = serializers.SerializerMethodField(label=_('Is success')) is_success = serializers.SerializerMethodField(label=_('Is success'))
asset = ObjectRelatedField(queryset=Asset.objects, label=_('Asset'))
account = ObjectRelatedField(queryset=Account.objects, label=_('Account'))
execution = ObjectRelatedField(
queryset=AutomationExecution.objects, label=_('Automation task execution')
)
class Meta: class Meta:
model = ChangeSecretRecord model = ChangeSecretRecord
fields = [ fields = [
'id', 'asset', 'account', 'date_started', 'date_finished', 'id', 'asset', 'account', 'date_started',
'is_success', 'error', 'execution', 'asset_display', 'account_display' 'date_finished', 'is_success', 'error', 'execution',
] ]
read_only_fields = fields read_only_fields = fields
@staticmethod
def get_asset_display(instance):
return str(instance.asset)
@staticmethod
def get_account_display(instance):
return str(instance.account)
@staticmethod @staticmethod
def get_is_success(obj): def get_is_success(obj):
if obj.status == 'success': if obj.status == 'success':

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.utils.translation import ugettext_lazy as _
from assets.models import GatherAccountsAutomation from assets.models import GatherAccountsAutomation
from common.utils import get_logger from common.utils import get_logger
@ -15,6 +16,9 @@ __all__ = [
class GatherAccountAutomationSerializer(BaseAutomationSerializer): class GatherAccountAutomationSerializer(BaseAutomationSerializer):
class Meta: class Meta:
model = GatherAccountsAutomation model = GatherAccountsAutomation
read_only_fields = BaseAutomationSerializer.Meta.read_only_fields read_only_fields = BaseAutomationSerializer.Meta.read_only_fields + ['executed_amount']
fields = BaseAutomationSerializer.Meta.fields + read_only_fields fields = BaseAutomationSerializer.Meta.fields + read_only_fields
extra_kwargs = BaseAutomationSerializer.Meta.extra_kwargs
extra_kwargs = {**BaseAutomationSerializer.Meta.extra_kwargs, **{
'executed_amount': {'label': _('Executed amount')}
}}

View File

@ -3,15 +3,17 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from common.drf.fields import EncryptedField
from assets.const import SecretType from assets.const import SecretType
from common.drf.fields import EncryptedField, LabeledChoiceField
from .utils import validate_password_for_ansible, validate_ssh_key from .utils import validate_password_for_ansible, validate_ssh_key
class AuthValidateMixin(serializers.Serializer): class AuthValidateMixin(serializers.Serializer):
secret_type = serializers.CharField(label=_('Secret type'), max_length=16, required=True) secret_type = LabeledChoiceField(
choices=SecretType.choices, required=True, label=_('Secret type')
)
secret = EncryptedField( secret = EncryptedField(
label=_('Secret'), required=False, max_length=16384, allow_blank=True, label=_('Secret'), required=False, max_length=40960, allow_blank=True,
allow_null=True, write_only=True, allow_null=True, write_only=True,
) )
passphrase = serializers.CharField( passphrase = serializers.CharField(

View File

@ -1,13 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import OrgResourceModelSerializerMixin from orgs.mixins.serializers import OrgResourceModelSerializerMixin
from ..models import GatheredUser from common.drf.fields import ObjectRelatedField
from ..models import GatheredUser, Asset
class GatheredUserSerializer(OrgResourceModelSerializerMixin): class GatheredUserSerializer(OrgResourceModelSerializerMixin):
asset = ObjectRelatedField(queryset=Asset.objects, label=_('Asset'))
class Meta: class Meta:
model = GatheredUser model = GatheredUser
fields_mini = ['id'] fields_mini = ['id']

View File

@ -8,6 +8,7 @@ from rest_framework.response import Response
from common.drf.api import JMSModelViewSet from common.drf.api import JMSModelViewSet
from common.permissions import IsServiceAccount from common.permissions import IsServiceAccount
from common.utils import is_uuid
from orgs.utils import tmp_to_builtin_org from orgs.utils import tmp_to_builtin_org
from rbac.permissions import RBACPermission from rbac.permissions import RBACPermission
from terminal.models import AppletHost from terminal.models import AppletHost
@ -63,6 +64,13 @@ class AppletHostAppletViewSet(HostMixin, JMSModelViewSet):
host: AppletHost host: AppletHost
serializer_class = AppletPublicationSerializer serializer_class = AppletPublicationSerializer
def get_object(self):
pk = self.kwargs.get('pk')
if not is_uuid(pk):
return self.host.publications.get(applet__name=pk)
else:
return self.host.publications.get(pk=pk)
def get_queryset(self): def get_queryset(self):
queryset = self.host.publications.all() queryset = self.host.publications.all()
return queryset return queryset

View File

@ -5,21 +5,24 @@ from rest_framework import serializers
from orgs.models import Organization from orgs.models import Organization
from orgs.utils import get_current_org_id from orgs.utils import get_current_org_id
from orgs.mixins.serializers import OrgResourceModelSerializerMixin from orgs.mixins.serializers import OrgResourceModelSerializerMixin
from common.drf.fields import LabeledChoiceField
from tickets.models import TicketFlow, ApprovalRule from tickets.models import TicketFlow, ApprovalRule
from tickets.const import TicketApprovalStrategy from tickets.const import TicketApprovalStrategy, TicketType
__all__ = ['TicketFlowSerializer'] __all__ = ['TicketFlowSerializer']
class TicketFlowApproveSerializer(serializers.ModelSerializer): class TicketFlowApproveSerializer(serializers.ModelSerializer):
strategy_display = serializers.ReadOnlyField(source='get_strategy_display', label=_('Approve strategy')) strategy = LabeledChoiceField(
choices=TicketApprovalStrategy.choices, required=True, label=_('Approve strategy')
)
assignees_read_only = serializers.SerializerMethodField(label=_('Assignees')) assignees_read_only = serializers.SerializerMethodField(label=_('Assignees'))
assignees_display = serializers.SerializerMethodField(label=_('Assignees display')) assignees_display = serializers.SerializerMethodField(label=_('Assignees display'))
class Meta: class Meta:
model = ApprovalRule model = ApprovalRule
fields_small = [ fields_small = [
'level', 'strategy', 'assignees_read_only', 'assignees_display', 'strategy_display' 'level', 'strategy', 'assignees_read_only', 'assignees_display',
] ]
fields_m2m = ['assignees', ] fields_m2m = ['assignees', ]
fields = fields_small + fields_m2m fields = fields_small + fields_m2m
@ -46,14 +49,16 @@ class TicketFlowApproveSerializer(serializers.ModelSerializer):
class TicketFlowSerializer(OrgResourceModelSerializerMixin): class TicketFlowSerializer(OrgResourceModelSerializerMixin):
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type display')) type = LabeledChoiceField(
choices=TicketType.choices, required=True, label=_('Type')
)
rules = TicketFlowApproveSerializer(many=True, required=True) rules = TicketFlowApproveSerializer(many=True, required=True)
class Meta: class Meta:
model = TicketFlow model = TicketFlow
fields_mini = ['id', ] fields_mini = ['id', ]
fields_small = fields_mini + [ fields_small = fields_mini + [
'type', 'type_display', 'approval_level', 'created_by', 'date_created', 'date_updated', 'type', 'approval_level', 'created_by', 'date_created', 'date_updated',
'org_id', 'org_name' 'org_id', 'org_name'
] ]
fields = fields_small + ['rules', ] fields = fields_small + ['rules', ]