Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

fix: 流程模板问题优化 #7626 #7635

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,3 +890,7 @@ 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
# 模板市场路由
TEMPLATE_MARKET_HOST = env.TEMPLATE_MARKET_HOST
# 模板市场文档路由
TEMPLATE_MARKET_DOC_URL = env.TEMPLATE_MARKET_DOC_URL
4 changes: 4 additions & 0 deletions env.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,7 @@
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", "")
# 模板市场路由
TEMPLATE_MARKET_HOST = os.getenv("TEMPLATE_MARKET_HOST", "")
# 模板市场文档路由
TEMPLATE_MARKET_DOC_URL = os.getenv("TEMPLATE_MARKET_DOC_URL", "")
42 changes: 22 additions & 20 deletions gcloud/contrib/template_market/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,47 @@
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""

import json
import requests

from gcloud.conf import settings


class MarketAPIClient:
def __init__(self):
def __init__(self, username):
self.base_url = settings.TEMPLATE_MARKET_API_URL
self.headers = {
"X-Bkapi-Authorization": json.dumps(
{"bk_app_code": settings.APP_CODE, "bk_app_secret": settings.SECRET_KEY, "bk_username": username}
),
"Content-Type": "application/json",
}

def _get_url(self, endpoint):
return f"{self.base_url}{endpoint}"

def get_service_category(self):
url = self._get_url("/category/get_service_category/")
response = requests.get(url)
def _make_request(self, method, endpoint, **kwargs):
url = self._get_url(endpoint)
response = requests.request(method, url, headers=self.headers, json=kwargs.get("data"))
return response.json()

def get_service_category(self):
return self._make_request("GET", "/category/get_service_category/")

def get_scene_label(self):
url = self._get_url("/sre_property/scene_label/")
response = requests.get(url)
return response.json()
return self._make_request("GET", "/sre_property/scene_label/")

def get_risk_level(self):
return self._make_request("GET", "/sre_scene/risk_level/")

def get_template_scene_detail(self, market_record_id):
url = self._get_url(f"/sre_scene/flow_template_scene/{market_record_id}/")
response = requests.get(url)
return response.json()
return self._make_request("GET", f"/sre_scene/flow_template_scene/{market_record_id}/")

def get_template_scene_list(self):
url = self._get_url("/sre_scene/flow_template_scene/?is_all=true")
response = requests.get(url)
return response.json()
return self._make_request("GET", "/sre_scene/flow_template_scene/?is_all=true")

def create_template_scene(self, data):
url = self._get_url("/sre_scene/flow_template_scene/")
response = requests.post(url, json=data)
return response.json()
return self._make_request("POST", "/sre_scene/flow_template_scene/", data=data)

def patch_template_scene(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()
return self._make_request("PATCH", f"/sre_scene/flow_template_scene/{market_record_id}/", data=data)
4 changes: 3 additions & 1 deletion gcloud/contrib/template_market/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 3.2.15 on 2024-12-13 10:48
# Generated by Django 3.2.15 on 2024-12-16 11:45

from django.db import migrations, models

Expand All @@ -17,6 +17,8 @@ class Migration(migrations.Migration):
("project_id", models.IntegerField(default=-1, help_text="项目 ID", verbose_name="项目 ID")),
("template_id", models.IntegerField(db_index=True, 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="创建时间")),
("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")),
("extra_info", models.JSONField(blank=True, null=True, verbose_name="额外信息")),
],
options={
Expand Down
2 changes: 2 additions & 0 deletions gcloud/contrib/template_market/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ class TemplateSharedRecord(models.Model):
project_id = models.IntegerField(_("项目 ID"), default=-1, help_text="项目 ID")
template_id = models.IntegerField(_("模板 ID"), help_text="模板 ID", db_index=True)
creator = models.CharField(_("创建者"), max_length=32, default="")
create_at = models.DateTimeField(verbose_name=_("创建时间"), auto_now_add=True)
update_at = models.DateTimeField(verbose_name=_("更新时间"), auto_now=True)
extra_info = models.JSONField(_("额外信息"), blank=True, null=True)

objects = TemplateSharedManager()
Expand Down
29 changes: 25 additions & 4 deletions gcloud/contrib/template_market/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

from rest_framework import serializers

from gcloud.taskflow3.models import TaskTemplate
from pipeline_web.constants import PWE

TEMPLATE_OPERATE_MAX_NUMBER = 10


class TemplatePreviewSerializer(serializers.Serializer):
name = serializers.CharField(read_only=True, help_text="模板名称")
Expand All @@ -30,14 +35,30 @@ class TemplateProjectBaseSerializer(serializers.Serializer):


class TemplateSharedRecordSerializer(serializers.Serializer):
project_id = serializers.CharField(required=True, max_length=32, help_text="项目id")
project_code = serializers.CharField(required=True, max_length=32, help_text="项目id")
template_ids = serializers.ListField(required=True, help_text="关联的模板列表")
creator = serializers.CharField(required=True, max_length=32, help_text="创建者")
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="共享标识")
category = serializers.CharField(required=True, help_text="共享分类")
risk_level = serializers.IntegerField(required=True, help_text="风险级别")
usage_id = serializers.IntegerField(required=True, help_text="使用说明id")
usage_id = serializers.IntegerField(required=False, help_text="使用说明id")
labels = serializers.ListField(child=serializers.IntegerField(), required=True, help_text="共享标签列表")
usage_content = serializers.JSONField(required=True, help_text="使用说明")

def validate_template_ids(self, value):
if not self.check_template_count_threshold(value):
raise serializers.ValidationError("The number of processes in the selected template exceeds the limit.")

return value

def check_template_count_threshold(self, template_id_list):
if len(template_id_list) > TEMPLATE_OPERATE_MAX_NUMBER:
return False

templates = TaskTemplate.objects.filter(id__in=template_id_list).select_related("pipeline_template")

acts = [act for tmpl in templates for act in tmpl.pipeline_template.data.get(PWE.activities, {}).values()]
# TODO: 目前只做了一层的数量统计
count = sum(act["type"] == PWE.SubProcess for act in acts)

return len(template_id_list) + count < TEMPLATE_OPERATE_MAX_NUMBER
4 changes: 2 additions & 2 deletions gcloud/contrib/template_market/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@

from django.conf.urls import include, url
from rest_framework.routers import DefaultRouter
from gcloud.contrib.template_market.viewsets import TemplatePreviewAPIView, SharedTemplateRecordsViewSet
from gcloud.contrib.template_market.viewsets import TemplatePreviewAPIView, TemplateSceneViewSet


template_market_router = DefaultRouter()
template_market_router.register(r"shared_templates_records", SharedTemplateRecordsViewSet)
template_market_router.register(r"templates_scene", TemplateSceneViewSet)

urlpatterns = [
url(r"^api/", include(template_market_router.urls)),
Expand Down
117 changes: 53 additions & 64 deletions gcloud/contrib/template_market/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,74 +51,67 @@ def get(self, request, *args, **kwargs):
return Response({"result": True, "data": serializer.data, "code": err_code.SUCCESS.code})


class SharedTemplateRecordsViewSet(viewsets.ViewSet):
class TemplateSceneViewSet(viewsets.ViewSet):
queryset = TemplateSharedRecord.objects.all()
serializer_class = TemplateSharedRecordSerializer
permission_classes = [permissions.IsAuthenticated, SharedTemplateRecordPermission]

market_client = MarketAPIClient()
market_client = MarketAPIClient

def _build_template_data(self, serializer, **kwargs):
templates = TaskTemplate.objects.filter(id__in=serializer.validated_data["template_ids"], is_deleted=False)
template_info = [{"id": template.id, "name": template.name} for template in templates]
data = {
"name": serializer.validated_data["name"],
"code": serializer.validated_data["code"],
"category": serializer.validated_data["category"],
"risk_level": serializer.validated_data["risk_level"],
"usage_id": serializer.validated_data["usage_id"],
"labels": serializer.validated_data["labels"],
"source_system": settings.APP_CODE,
"project_code": serializer.validated_data["project_id"],
"templates": json.dumps(template_info),
"usage_content": serializer.validated_data["usage_content"],
}
data = {"source_system": settings.APP_CODE, "templates": json.dumps(template_info), **serializer.validated_data}
market_record_id = kwargs.get("market_record_id")
if market_record_id:
data["id"] = market_record_id
return data

@action(detail=False, methods=["get"])
def get_service_category(self, request, *args, **kwargs):
response_data = self.market_client.get_service_category()
if not response_data["result"]:
logging.warning("Failed to obtain the market service category")
def _handle_response(self, response_data, error_message):
if not response_data.get("result"):
logging.exception(error_message)
return Response(
{
"result": False,
"message": "Failed to obtain the market service category",
"message": error_message,
"code": err_code.OPERATION_FAIL.code,
}
)
return None

@action(detail=False, methods=["get"])
def get_service_category(self, request, *args, **kwargs):
client = self.market_client(username=request.user.username)
response_data = client.get_service_category()
error_response = self._handle_response(response_data, "Failed to obtain scene category")
if error_response:
return error_response
return Response({"result": True, "data": response_data["data"], "code": err_code.SUCCESS.code})

@action(detail=False, methods=["get"])
def get_scene_label(self, request, *args, **kwargs):
response_data = self.market_client.get_scene_label()
client = self.market_client(username=request.user.username)
response_data = client.get_scene_label()
error_response = self._handle_response(response_data, "Failed to obtain scene tag list")
if error_response:
return error_response
return Response({"result": True, "data": response_data["data"], "code": err_code.SUCCESS.code})

if not response_data["result"]:
logging.exception("Failed to obtain scene tag list")
return Response(
{
"result": False,
"message": "Failed to obtain scene tag list",
"code": err_code.OPERATION_FAIL.code,
}
)
@action(detail=False, methods=["get"])
def get_risk_level(self, request, *args, **kwargs):
client = self.market_client(username=request.user.username)
response_data = client.get_risk_level()
error_response = self._handle_response(response_data, "Failed to obtain the market risk level list")
if error_response:
return error_response
return Response({"result": True, "data": response_data["data"], "code": err_code.SUCCESS.code})

def list(self, request, *args, **kwargs):
response_data = self.market_client.get_template_scene_list()

if not response_data["result"]:
logging.exception("Failed to obtain the market template list")
return Response(
{
"result": False,
"message": "Failed to obtain the market template list",
"code": err_code.OPERATION_FAIL.code,
}
)
client = self.market_client(username=request.user.username)
response_data = client.get_template_scene_list()
error_response = self._handle_response(response_data, "Failed to obtain the market template list")
if error_response:
return error_response
return Response({"result": True, "data": response_data, "code": err_code.SUCCESS.code})

@swagger_auto_schema(request_body=TemplateSharedRecordSerializer)
Expand All @@ -127,20 +120,17 @@ def create(self, request, *args, **kwargs):
serializer.is_valid(raise_exception=True)

data = self._build_template_data(serializer)
response_data = self.market_client.create_template_scene(data)
if not response_data.get("result"):
return Response(
{
"result": False,
"message": "Failed to create market template record",
"code": err_code.OPERATION_FAIL.code,
}
)
client = self.market_client(username=request.user.username)
response_data = client.create_template_scene(data)
error_response = self._handle_response(response_data, "Failed to create market template record")
if error_response:
return error_response

TemplateSharedRecord.objects.update_shared_record(
project_id=int(serializer.validated_data["project_id"]),
project_id=int(serializer.validated_data["project_code"]),
new_template_ids=serializer.validated_data["template_ids"],
market_record_id=response_data["data"]["id"],
creator=serializer.validated_data["creator"],
creator=request.user.username,
)
return Response({"result": True, "data": response_data, "code": err_code.SUCCESS.code})

Expand All @@ -149,25 +139,24 @@ def partial_update(self, request, *args, **kwargs):
market_record_id = kwargs["pk"]
serializer = self.serializer_class(data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
existing_records = self.market_client.get_template_scene_detail(market_record_id)

client = self.market_client(username=request.user.username)
existing_records = client.get_template_scene_detail(market_record_id)
existing_market_template_ids = set(
[template["id"] for template in json.loads(existing_records["data"]["templates"])]
)

data = self._build_template_data(serializer, market_record_id=market_record_id)
response_data = self.market_client.patch_template_scene(data, market_record_id)
if not response_data.get("result"):
return Response(
{
"result": False,
"message": "Failed to update market template record",
"code": err_code.OPERATION_FAIL.code,
}
)
response_data = client.patch_template_scene(data, market_record_id)
error_response = self._handle_response(response_data, "Failed to update market template record")
if error_response:
return error_response

TemplateSharedRecord.objects.update_shared_record(
project_id=int(serializer.validated_data["project_id"]),
project_id=int(serializer.validated_data["project_code"]),
new_template_ids=serializer.validated_data["template_ids"],
market_record_id=market_record_id,
creator=serializer.validated_data["creator"],
creator=request.user.username,
existing_market_template_ids=existing_market_template_ids,
)
return Response({"result": True, "data": response_data, "code": err_code.SUCCESS.code})
3 changes: 3 additions & 0 deletions gcloud/core/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ def mysetting(request):
"RUN_VER_NAME": EnvironmentVariables.objects.get_var(run_ver_key, settings.RUN_VER_NAME),
"REMOTE_ANALYSIS_URL": settings.REMOTE_ANALYSIS_URL,
"REMOTE_API_URL": settings.REMOTE_API_URL,
"ENABLE_TEMPLATE_MARKET": settings.ENABLE_TEMPLATE_MARKET,
"TEMPLATE_MARKET_HOST": settings.TEMPLATE_MARKET_HOST,
"TEMPLATE_MARKET_DOC_URL": settings.TEMPLATE_MARKET_DOC_URL,
"USERNAME": request.user.username,
# 'NICK': request.session.get('nick', ''), # 用户昵称
"NICK": request.user.username, # 用户昵称
Expand Down
Loading