diff --git a/config/default.py b/config/default.py index c771f2f3f..be7bbd0ad 100644 --- a/config/default.py +++ b/config/default.py @@ -890,5 +890,3 @@ def check_engine_admin_permission(request, *args, **kwargs): ENABLE_TEMPLATE_MARKET = env.ENABLE_TEMPLATE_MARKET # 流程商店 API 地址 TEMPLATE_MARKET_API_URL = env.TEMPLATE_MARKET_API_URL -# 共享流程最大数量 -MAX_NUMBER_SHARED_PROCESSES = env.MAX_NUMBER_SHARED_PROCESSES diff --git a/env.py b/env.py index cab2ee11d..2a4e1546e 100644 --- a/env.py +++ b/env.py @@ -158,5 +158,3 @@ ENABLE_TEMPLATE_MARKET = False if os.getenv("ENABLE_TEMPLATE_MARKET") is None else True # 流程商店 API 地址 TEMPLATE_MARKET_API_URL = os.getenv("TEMPLATE_MARKET_API_URL", "") -# 共享流程最大数量 -MAX_NUMBER_SHARED_PROCESSES = int(os.getenv("MAX_NUMBER_SHARED_PROCESSES", 10)) diff --git a/gcloud/apigw/management/commands/data/api-resources.yml b/gcloud/apigw/management/commands/data/api-resources.yml index cdabd3892..b5712b7c8 100644 --- a/gcloud/apigw/management/commands/data/api-resources.yml +++ b/gcloud/apigw/management/commands/data/api-resources.yml @@ -762,13 +762,13 @@ paths: operationId: copy_template_across_project description: 跨业务复制模板 tags: - - 通用接口 + - 限制接口 responses: default: description: '' x-bk-apigateway-resource: - isPublic: false - allowApplyPermission: true + isPublic: true + allowApplyPermission: false matchSubpath: false backend: type: HTTP diff --git a/gcloud/apigw/views/copy_template_across_project.py b/gcloud/apigw/views/copy_template_across_project.py index d7673992c..40366ae9f 100644 --- a/gcloud/apigw/views/copy_template_across_project.py +++ b/gcloud/apigw/views/copy_template_across_project.py @@ -60,7 +60,7 @@ def copy_template_across_project(request, project_id): operator=request.user.username, ) except Exception as e: - logger.exception("The process fails to be replicated across services: {}".format(e)) + logger.exception("The template fails to be copied across project: {}".format(e)) return { "result": False, "message": "invalid flow data or error occur, please contact administrator", diff --git a/gcloud/contrib/template_market/admin.py b/gcloud/contrib/template_market/admin.py index 9083b78bd..6b5429b3d 100644 --- a/gcloud/contrib/template_market/admin.py +++ b/gcloud/contrib/template_market/admin.py @@ -18,6 +18,6 @@ @admin.register(models.TemplateSharedRecord) class TemplateSharedRecordAdmin(admin.ModelAdmin): - list_display = ["scene_shared_id", "project_id", "templates", "creator", "create_at", "update_at", "extra_info"] + list_display = ["market_record_id", "project_id", "templates", "creator", "create_at", "update_at", "extra_info"] list_filter = ["project_id", "creator", "create_at", "update_at"] - search_fields = ["scene_shared_id", "project_id", "creator"] + search_fields = ["market_record_id", "project_id", "creator"] diff --git a/gcloud/contrib/template_market/utils.py b/gcloud/contrib/template_market/clients.py similarity index 80% rename from gcloud/contrib/template_market/utils.py rename to gcloud/contrib/template_market/clients.py index 359b333fd..97cafd2da 100644 --- a/gcloud/contrib/template_market/utils.py +++ b/gcloud/contrib/template_market/clients.py @@ -23,17 +23,17 @@ def __init__(self): def _get_url(self, endpoint): return f"{self.base_url}{endpoint}" - def get_template_list(self): - url = self._get_url("/sre_scene/flow_template_scene/") + def get_market_template_list(self): + url = self._get_url("/sre_scene/flow_template_scene/?is_all=true") response = requests.get(url) return response.json() - def create_template(self, data): + def create_market_template_record(self, data): url = self._get_url("/sre_scene/flow_template_scene/") response = requests.post(url, json=data) return response.json() - def patch_template(self, data, scene_shared_id): - url = self._get_url(f"/sre_scene/flow_template_scene/{scene_shared_id}/") + def patch_market_template_record(self, data, market_record_id): + url = self._get_url(f"/sre_scene/flow_template_scene/{market_record_id}/") response = requests.patch(url, json=data) return response.json() diff --git a/gcloud/contrib/template_market/migrations/0001_initial.py b/gcloud/contrib/template_market/migrations/0001_initial.py index 50b0871bd..b4b5c07fd 100644 --- a/gcloud/contrib/template_market/migrations/0001_initial.py +++ b/gcloud/contrib/template_market/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.15 on 2024-12-11 13:46 +# Generated by Django 3.2.15 on 2024-12-12 09:15 from django.db import migrations, models @@ -14,8 +14,8 @@ class Migration(migrations.Migration): name="TemplateSharedRecord", fields=[ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), - ("scene_shared_id", models.IntegerField(help_text="共享实例id", verbose_name="共享实例id")), - ("project_id", models.IntegerField(default=-1, help_text="项目 ID", verbose_name="项目 ID")), + ("market_record_id", models.CharField(db_index=True, max_length=32, verbose_name="模板市场记录 ID")), + ("project_id", models.IntegerField(help_text="项目 ID", verbose_name="项目 ID")), ("templates", models.JSONField(help_text="模板 ID 列表", verbose_name="模板 ID 列表")), ("creator", models.CharField(default="", max_length=32, verbose_name="创建者")), ("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")), diff --git a/gcloud/contrib/template_market/models.py b/gcloud/contrib/template_market/models.py index f16eaf7ab..31b4e6f84 100644 --- a/gcloud/contrib/template_market/models.py +++ b/gcloud/contrib/template_market/models.py @@ -16,7 +16,7 @@ class TemplateSharedRecord(models.Model): - scene_shared_id = models.IntegerField(_("共享实例id"), help_text="共享实例id") + market_record_id = models.CharField(_("共享实例 ID"), max_length=32, help_text="共享实例 ID", db_index=True) project_id = models.IntegerField(_("项目 ID"), default=-1, help_text="项目 ID") templates = models.JSONField(_("模板 ID 列表"), help_text="模板 ID 列表") creator = models.CharField(_("创建者"), max_length=32, default="") diff --git a/gcloud/contrib/template_market/permission.py b/gcloud/contrib/template_market/permission.py index bcd3e4ba4..e14739196 100644 --- a/gcloud/contrib/template_market/permission.py +++ b/gcloud/contrib/template_market/permission.py @@ -12,27 +12,24 @@ """ import logging -from django.db.models import Q +from iam.exceptions import MultiAuthFailedException from rest_framework import permissions from gcloud.conf import settings from gcloud.contrib.template_market.models import TemplateSharedRecord +from gcloud.contrib.template_market.serializers import TemplateProjectBaseSerializer from gcloud.iam_auth import IAMMeta from gcloud.iam_auth.utils import iam_multi_resource_auth_or_raise class TemplatePreviewPermission(permissions.BasePermission): def has_permission(self, request, view): - try: - template_id = int(request.GET.get("template_id")) - project_id = int(request.GET.get("project_id")) - except (TypeError, ValueError): - logging.warning("Missing or invalid required parameters.") - return False + serializer = TemplateProjectBaseSerializer(data=request.GET) + serializer.is_valid(raise_exception=True) - record = TemplateSharedRecord.objects.filter( - Q(project_id=project_id) & Q(templates__contains=[template_id]) - ).first() + template_id = int(serializer.validated_data["template_id"]) + project_id = int(serializer.validated_data["project_id"]) + record = TemplateSharedRecord.objects.filter(project_id=project_id, templates__contains=[template_id]).first() if record is None: logging.warning("The specified template could not be found") return False @@ -42,15 +39,21 @@ def has_permission(self, request, view): class SharedProcessTemplatePermission(permissions.BasePermission): def has_permission(self, request, view): + if not settings.ENABLE_TEMPLATE_MARKET: + return False + if view.action in ["create", "partial_update"]: username = request.user.username serializer = view.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) template_id_list = serializer.validated_data["templates"] + try: + iam_multi_resource_auth_or_raise( + username, IAMMeta.FLOW_EDIT_ACTION, template_id_list, "resources_list_for_flows" + ) + except MultiAuthFailedException: + logging.exception("You do not have permission to perform this operation") + return False - iam_multi_resource_auth_or_raise( - username, IAMMeta.FLOW_EDIT_ACTION, template_id_list, "resources_list_for_flows" - ) - - return settings.ENABLE_TEMPLATE_MARKET + return True diff --git a/gcloud/contrib/template_market/serializers.py b/gcloud/contrib/template_market/serializers.py index 3d030e99f..82248119f 100644 --- a/gcloud/contrib/template_market/serializers.py +++ b/gcloud/contrib/template_market/serializers.py @@ -13,7 +13,6 @@ import json from rest_framework import serializers -from gcloud.constants import DATETIME_FORMAT from gcloud.contrib.template_market.models import TemplateSharedRecord @@ -34,8 +33,6 @@ class TemplateSharedRecordSerializer(serializers.ModelSerializer): project_id = serializers.CharField(required=True, max_length=32, help_text="项目id") templates = serializers.ListField(required=True, help_text="关联的模板列表") creator = serializers.CharField(required=True, max_length=32, help_text="创建者") - create_at = serializers.DateTimeField(required=False, help_text="创建时间", format=DATETIME_FORMAT) - update_at = serializers.DateTimeField(required=False, help_text="更新时间", format=DATETIME_FORMAT) extra_info = serializers.JSONField(required=False, allow_null=True, help_text="额外信息") name = serializers.CharField(required=True, help_text="共享名称") code = serializers.CharField(required=True, help_text="共享标识") @@ -51,8 +48,6 @@ class Meta: "project_id", "templates", "creator", - "create_at", - "update_at", "extra_info", "name", "code", diff --git a/gcloud/contrib/template_market/viewsets.py b/gcloud/contrib/template_market/viewsets.py index bd0f790cd..ba670d2cc 100644 --- a/gcloud/contrib/template_market/viewsets.py +++ b/gcloud/contrib/template_market/viewsets.py @@ -18,7 +18,6 @@ from rest_framework import permissions from gcloud import err_code -from gcloud.conf import settings from drf_yasg.utils import swagger_auto_schema from gcloud.contrib.template_market.serializers import ( TemplateSharedRecordSerializer, @@ -27,7 +26,7 @@ ) from gcloud.contrib.template_market.models import TemplateSharedRecord from gcloud.taskflow3.models import TaskTemplate -from gcloud.contrib.template_market.utils import MarketAPIClient +from gcloud.contrib.template_market.clients import MarketAPIClient from gcloud.contrib.template_market.permission import TemplatePreviewPermission, SharedProcessTemplatePermission @@ -78,25 +77,18 @@ def _build_template_data(self, serializer, **kwargs): data["id"] = scene_shared_id return data - def _get_processes_count(self, template_id_list): - template_objs = TaskTemplate.objects.filter(id__in=template_id_list) - - total_count = 0 - for template in template_objs: - activities = template.pipeline_tree.get("activities", []) - total_count += len(activities) - - return total_count - def list(self, request, *args, **kwargs): - response_data = self.market_client.get_template_list() + response_data = self.market_client.get_market_template_list() if not response_data["result"]: - logging.exception(f"Get template information from market failed, error code: {response_data.get('code')}") + logging.exception("Failed to obtain the market template list") return Response( - {"result": False, "message": "Get template information failed", "code": err_code.OPERATION_FAIL.code} + { + "result": False, + "message": "Failed to obtain the market template list", + "code": err_code.OPERATION_FAIL.code, + } ) - return Response({"result": True, "data": response_data, "code": err_code.SUCCESS.code}) @swagger_auto_schema(request_body=TemplateSharedRecordSerializer) @@ -104,70 +96,36 @@ def create(self, request, *args, **kwargs): serializer = self.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) - template_count = self._get_processes_count(serializer.validated_data["templates"]) - if template_count > settings.MAX_NUMBER_SHARED_PROCESSES: + data = self._build_template_data(serializer) + response_data = self.market_client.create_market_template_record(data) + if not response_data.get("result"): return Response( { "result": False, - "message": "The number of selected templates exceeds the limit", + "message": "Failed to create market template record", "code": err_code.OPERATION_FAIL.code, } ) - - data = self._build_template_data(serializer) - try: - response_data = self.market_client.create_template(data) - if not response_data.get("result"): - return Response( - { - "result": False, - "message": "Failed to share template to sre store", - "code": err_code.OPERATION_FAIL.code, - } - ) - serializer.validated_data["scene_shared_id"] = response_data["data"]["id"] - serializer.save() - return Response( - { - "result": True, - "data": "response_data", - "message": "Share template successfully", - "code": err_code.SUCCESS.code, - } - ) - except Exception as e: - logging.exception("Share template failed: %s", e) - return Response({"result": False, "message": "Share template failed", "code": err_code.OPERATION_FAIL.code}) + serializer.validated_data["market_record_id"] = response_data["data"]["id"] + serializer.create(serializer.validated_data) + return Response({"result": True, "data": response_data, "code": err_code.SUCCESS.code}) @swagger_auto_schema(request_body=TemplateSharedRecordSerializer) def partial_update(self, request, *args, **kwargs): - scene_shared_id = kwargs["pk"] - instance = self.queryset.get(scene_shared_id=scene_shared_id) + market_record_id = kwargs["pk"] + instance = self.queryset.get(market_record_id=market_record_id) serializer = self.serializer_class(data=request.data, partial=True) serializer.is_valid(raise_exception=True) - data = self._build_template_data(serializer, scene_shared_id=scene_shared_id) - try: - response_data = self.market_client.patch_template(data, scene_shared_id) - if not response_data.get("result"): - return Response( - { - "result": False, - "message": "Update template to sre store failed", - "code": err_code.OPERATION_FAIL.code, - } - ) - serializer.update(instance, validated_data=serializer.validated_data) + data = self._build_template_data(serializer, market_record_id=market_record_id) + response_data = self.market_client.patch_market_template_record(data, market_record_id) + if not response_data.get("result"): return Response( { - "result": True, - "data": response_data, - "message": "Share template successfully", - "code": err_code.SUCCESS.code, + "result": False, + "message": "Failed to update market template record", + "code": err_code.OPERATION_FAIL.code, } ) - except Exception as e: - logging.exception("Failed to update scene template: %s", e) - return Response( - {"result": False, "message": "Failed to update scene template", "code": err_code.OPERATION_FAIL.code} - ) + serializer.update(instance=instance, validated_data=serializer.validated_data) + return Response({"result": True, "data": response_data, "code": err_code.SUCCESS.code})