From 1358cf532f929f2d956ae0e62b73bb8c01942894 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 18 Dec 2023 17:56:16 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20labels=20=E5=92=8C?= =?UTF-8?q?=20role=20=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/serializers/fields.py | 5 ++++- apps/labels/api.py | 29 +++++++++-------------------- apps/labels/const.py | 24 ++++++++++++++++++++++++ apps/labels/models.py | 4 +++- apps/labels/serializers.py | 21 ++++++++++++++++----- apps/ops/api/job.py | 7 ++++--- apps/ops/models/job.py | 9 ++------- apps/ops/models/playbook.py | 3 +-- apps/rbac/api/role.py | 13 +++++++++++++ 9 files changed, 76 insertions(+), 39 deletions(-) diff --git a/apps/common/serializers/fields.py b/apps/common/serializers/fields.py index bb3bafbf6..e206afcb3 100644 --- a/apps/common/serializers/fields.py +++ b/apps/common/serializers/fields.py @@ -59,7 +59,10 @@ class EncryptedField(serializers.CharField): class LabeledChoiceField(ChoiceField): def __init__(self, *args, **kwargs): super(LabeledChoiceField, self).__init__(*args, **kwargs) - self.choice_mapper = { + + @property + def choice_mapper(self): + return { key: value for key, value in self.choices.items() } diff --git a/apps/labels/api.py b/apps/labels/api.py index 26ef68685..24c1d0463 100644 --- a/apps/labels/api.py +++ b/apps/labels/api.py @@ -9,9 +9,10 @@ from orgs.utils import current_org from rbac.models import ContentType from rbac.serializers import ContentTypeSerializer from . import serializers +from .const import label_resource_types from .models import Label, LabeledResource -__all__ = ['LabelViewSet'] +__all__ = ['LabelViewSet', 'ContentTypeViewSet'] class ContentTypeViewSet(JMSModelViewSet): @@ -25,26 +26,8 @@ class ContentTypeViewSet(JMSModelViewSet): can_labeled_content_type = [] model = ContentType - @classmethod - def get_can_labeled_content_type_ids(cls): - if cls.can_labeled_content_type: - return cls.can_labeled_content_type - content_types = ContentType.objects.all() - for ct in content_types: - model_cls = ct.model_class() - if not model_cls: - continue - if model_cls._meta.parents: - continue - if 'labels' in model_cls._meta._forward_fields_map.keys(): - # if issubclass(model_cls, LabeledMixin): - cls.can_labeled_content_type.append(ct.id) - return cls.can_labeled_content_type - def get_queryset(self): - ids = self.get_can_labeled_content_type_ids() - queryset = ContentType.objects.filter(id__in=ids) - return queryset + return label_resource_types @action(methods=['GET'], detail=True, serializer_class=serializers.ContentTypeResourceSerializer) def resources(self, request, *args, **kwargs): @@ -62,6 +45,7 @@ class ContentTypeViewSet(JMSModelViewSet): keyword = request.query_params.get('search') if keyword: queryset = content_type.filter_queryset(queryset, keyword) + queryset = queryset.order_by('res_type') return self.get_paginated_response_from_queryset(queryset) @@ -145,3 +129,8 @@ class LabeledResourceViewSet(OrgBulkModelViewSet): 'default': serializers.LabeledResourceSerializer, } ordering_fields = ('res_type', 'date_created') + + def get_queryset(self): + queryset = super().get_queryset() + queryset = queryset.order_by('res_type') + return queryset diff --git a/apps/labels/const.py b/apps/labels/const.py index e69de29bb..cf7038235 100644 --- a/apps/labels/const.py +++ b/apps/labels/const.py @@ -0,0 +1,24 @@ +from django.contrib.contenttypes.models import ContentType +from django.utils.functional import LazyObject + + +class LabeledResourceType(LazyObject): + @staticmethod + def get_res_types(): + content_types = ContentType.objects.all() + ids = [] + for ct in content_types: + model_cls = ct.model_class() + if not model_cls: + continue + if model_cls._meta.parents: + continue + if 'labels' in model_cls._meta._forward_fields_map.keys(): + ids.append(ct.id) + return ContentType.objects.filter(id__in=ids) + + def _setup(self): + self._wrapped = self.get_res_types() + + +label_resource_types = LabeledResourceType() diff --git a/apps/labels/models.py b/apps/labels/models.py index 3e38da48e..9d06f76e3 100644 --- a/apps/labels/models.py +++ b/apps/labels/models.py @@ -25,7 +25,9 @@ class Label(JMSOrgBaseModel): class LabeledResource(JMSOrgBaseModel): - label = models.ForeignKey(Label, on_delete=models.CASCADE, related_name='labeled_resources') + label = models.ForeignKey( + Label, on_delete=models.CASCADE, related_name='labeled_resources', verbose_name=_("Label") + ) res_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) res_id = models.CharField(max_length=36, verbose_name=_("Resource ID"), db_index=True) resource = GenericForeignKey('res_type', 'res_id') diff --git a/apps/labels/serializers.py b/apps/labels/serializers.py index 157afc92e..60b0ad4b1 100644 --- a/apps/labels/serializers.py +++ b/apps/labels/serializers.py @@ -1,10 +1,10 @@ -from django.contrib.contenttypes.models import ContentType from django.db.models import Count from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -from common.serializers.fields import ObjectRelatedField +from common.serializers.fields import ObjectRelatedField, LabeledChoiceField from orgs.mixins.serializers import BulkOrgResourceModelSerializer +from .const import label_resource_types from .models import Label, LabeledResource __all__ = ['LabelSerializer', 'LabeledResourceSerializer', 'ContentTypeResourceSerializer'] @@ -27,10 +27,10 @@ class LabelSerializer(BulkOrgResourceModelSerializer): class LabeledResourceSerializer(serializers.ModelSerializer): - res_type = ObjectRelatedField( - queryset=ContentType.objects, attrs=('app_label', 'model', 'name'), label=_("Resource type") + res_type = LabeledChoiceField( + choices=[], label=_("Resource type"), source='res_type_id', required=False ) - label = ObjectRelatedField(queryset=Label.objects, attrs=('name', 'value')) + label = ObjectRelatedField(queryset=Label.objects, attrs=('name', 'value'), label=_("Label")) resource = serializers.CharField(label=_("Resource")) class Meta: @@ -44,6 +44,17 @@ class LabeledResourceSerializer(serializers.ModelSerializer): queryset = queryset.select_related('label', 'res_type') return queryset + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_res_type_choices() + + def set_res_type_choices(self): + res_type_field = self.fields.get('res_type') + if not res_type_field: + return + + res_type_field.choices = [(ct.id, ct.name) for ct in label_resource_types] + class ContentTypeResourceSerializer(serializers.Serializer): id = serializers.CharField() diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index 3b07e850d..f16fefde2 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -1,12 +1,12 @@ import json import os + from django.conf import settings from django.db import transaction from django.db.models import Count from django.shortcuts import get_object_or_404 from django.utils._os import safe_join from django.utils.translation import gettext_lazy as _ - from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.views import APIView @@ -167,7 +167,7 @@ class JobExecutionViewSet(OrgBulkModelViewSet): @staticmethod def start_deploy(instance, serializer): - task = run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id)) + run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id)) def perform_create(self, serializer): instance = serializer.save() @@ -179,7 +179,8 @@ class JobExecutionViewSet(OrgBulkModelViewSet): set_task_to_serializer_data(serializer, instance.id) transaction.on_commit( - lambda: run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id))) + lambda: run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id)) + ) def get_queryset(self): queryset = super().get_queryset() diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index bf460232f..9fcc079ca 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -22,7 +22,6 @@ from acls.models import CommandFilterACL from assets.models import Asset from assets.automations.base.manager import SSHTunnelManager from common.db.encoder import ModelJSONFieldEncoder -from labels.mixins import LabeledMixin from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException, UploadFileRunner from ops.mixin import PeriodTaskModelMixin from ops.variables import * @@ -133,19 +132,15 @@ class JMSPermedInventory(JMSInventory): return mapper -class Job(LabeledMixin, JMSOrgBaseModel, PeriodTaskModelMixin): +class Job(JMSOrgBaseModel, PeriodTaskModelMixin): name = models.CharField(max_length=128, null=True, verbose_name=_('Name')) - instant = models.BooleanField(default=False) args = models.CharField(max_length=8192, default='', verbose_name=_('Args'), null=True, blank=True) module = models.CharField(max_length=128, choices=JobModules.choices, default=JobModules.shell, - verbose_name=_('Module'), - null=True) + verbose_name=_('Module'), null=True) chdir = models.CharField(default="", max_length=1024, verbose_name=_('Chdir'), null=True, blank=True) timeout = models.IntegerField(default=-1, verbose_name=_('Timeout (Seconds)')) - playbook = models.ForeignKey('ops.Playbook', verbose_name=_("Playbook"), null=True, on_delete=models.SET_NULL) - type = models.CharField(max_length=128, choices=Types.choices, default=Types.adhoc, verbose_name=_("Type")) creator = models.ForeignKey('users.User', verbose_name=_("Creator"), on_delete=models.SET_NULL, null=True) assets = models.ManyToManyField('assets.Asset', verbose_name=_("Assets")) diff --git a/apps/ops/models/playbook.py b/apps/ops/models/playbook.py index caf226ee6..e1188cb46 100644 --- a/apps/ops/models/playbook.py +++ b/apps/ops/models/playbook.py @@ -6,7 +6,6 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from private_storage.fields import PrivateFileField -from labels.mixins import LabeledMixin from ops.const import CreateMethods from ops.exception import PlaybookNoValidEntry from orgs.mixins.models import JMSOrgBaseModel @@ -24,7 +23,7 @@ dangerous_keywords = ( ) -class Playbook(LabeledMixin, JMSOrgBaseModel): +class Playbook(JMSOrgBaseModel): id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, verbose_name=_('Name'), null=True) path = PrivateFileField(upload_to='playbooks/') diff --git a/apps/rbac/api/role.py b/apps/rbac/api/role.py index 387d0bc9c..093c79206 100644 --- a/apps/rbac/api/role.py +++ b/apps/rbac/api/role.py @@ -56,9 +56,22 @@ class RoleViewSet(JMSModelViewSet): return instance.permissions.set(clone.get_permissions()) + def filter_builtins(self, queryset): + keyword = self.request.query_params.get('search') + if not keyword: + return queryset + + builtins = list(self.queryset.filter(builtin=True)) + matched = [role.id for role in builtins if keyword in role.display_name] + if not matched: + return queryset + queryset = list(queryset.exclude(id__in=matched)) + return queryset + builtins + def filter_queryset(self, queryset): queryset = super().filter_queryset(queryset) queryset = queryset.order_by(*self.ordering) + queryset = self.filter_builtins(queryset) return queryset def set_users_amount(self, queryset):