Skip to content

Commit

Permalink
fix: 修复逻辑问题 TencentBlueKing#7626
Browse files Browse the repository at this point in the history
  • Loading branch information
guohelu committed Dec 10, 2024
1 parent 39204d0 commit cbb17a8
Show file tree
Hide file tree
Showing 13 changed files with 269 additions and 93 deletions.
2 changes: 0 additions & 2 deletions config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
# 流程跨业务复制
ENABLE_APIGW_COPY_TEMPLATE = env.ENABLE_APIGW_COPY_TEMPLATE
2 changes: 0 additions & 2 deletions env.py
Original file line number Diff line number Diff line change
Expand Up @@ -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", "")
# 流程跨业务复制
ENABLE_APIGW_COPY_TEMPLATE = False if os.getenv("ENABLE_APIGW_COPY_TEMPLATE") is None else True
24 changes: 24 additions & 0 deletions gcloud/apigw/management/commands/data/api-resources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -757,3 +757,27 @@ paths:
authConfig:
userVerifiedRequired: true
disabledStages: []
/copy_template_across_project/{project_id}/:
post:
operationId: copy_template_across_project
description: 跨业务复制模板
tags:
- 通用接口
responses:
default:
description: ''
x-bk-apigateway-resource:
isPublic: true
allowApplyPermission: true
matchSubpath: false
backend:
type: HTTP
method: post
path: /{env.api_sub_path}apigw/copy_template_across_project/{project_id>}/
matchSubpath: false
timeout: 0
upstreams: {}
transformHeaders: {}
authConfig:
userVerifiedRequired: true
disabledStages: []
4 changes: 2 additions & 2 deletions gcloud/apigw/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
from gcloud.apigw.views.register_project import register_project
from gcloud.apigw.views.set_periodic_task_enabled import set_periodic_task_enabled
from gcloud.apigw.views.start_task import start_task
from gcloud.apigw.views.copy_template_across_biz import copy_template_across_biz
from gcloud.apigw.views.copy_template_across_project import copy_template_across_project

urlpatterns = [
url(r"^dispatch_plugin_query/$", dispatch_plugin_query),
Expand Down Expand Up @@ -130,5 +130,5 @@
url(r"^create_clocked_task/(?P<template_id>\d+)/(?P<project_id>\d+)/$", create_clocked_task),
url(r"^get_mini_app_list/(?P<project_id>\d+)/$", get_mini_app_list),
url(r"^get_task_count/(?P<project_id>\d+)/$", get_task_count),
url(r"^copy_template_across_biz/(?P<project_id>\d+)/$", copy_template_across_biz),
url(r"^copy_template_across_project/(?P<project_id>\d+)/$", copy_template_across_project),
]
28 changes: 28 additions & 0 deletions gcloud/apigw/validators/copy_template_across_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
"""
Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community
Edition) available.
Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
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

from gcloud.utils.validate import ObjectJsonBodyValidator


class CopyTemplateAcrossProjectValidator(ObjectJsonBodyValidator):
def validate(self, request, *args, **kwargs):
valid, err = super().validate(request, *args, **kwargs)

if not valid:
return valid, err

if not json.loads(request.body).get("new_project_id") or not json.loads(request.body).get("template_id"):
return False, "new_project_id and template_id is required"

return True, ""
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@
return_json_response,
)

from gcloud.conf import settings
from gcloud.apigw.views.utils import logger
from gcloud.tasktmpl3.models import TaskTemplate
from gcloud.template_base.utils import format_import_result_to_response_data
from gcloud.utils.decorators import request_validate
from gcloud.apigw.validators.copy_template_across_project import CopyTemplateAcrossProjectValidator


@login_exempt
Expand All @@ -36,39 +37,30 @@
@apigw_require
@return_json_response
@project_inject
@request_validate(CopyTemplateAcrossProjectValidator)
@mark_request_whether_is_trust
def copy_template_across_biz(request, project_id):
if not request.is_trust or not settings.ENABLE_APIGW_COPY_TEMPLATE:
def copy_template_across_project(request, project_id):
if not request.is_trust:
return {
"result": False,
"message": "you have no permission to call this api.",
"code": err_code.REQUEST_FORBIDDEN_INVALID.code,
}

params_data = json.loads(request.body)
new_project_id = params_data.get("new_project_id")
template_id = params_data.get("template_id")
template_id_list = [template_id] if template_id is not None else []

if not new_project_id or not template_id_list:
return {
"result": False,
"message": "missing or invalid parameter",
"code": err_code.REQUEST_PARAM_INVALID.code,
}
new_project_id = params_data["new_project_id"]
template_id = params_data["template_id"]

try:
export_data = TaskTemplate.objects.export_templates(
template_id_list, is_full=False, project_id=request.project.id
)
export_data = TaskTemplate.objects.export_templates([template_id], is_full=False, project_id=request.project.id)
import_result = TaskTemplate.objects.import_templates(
template_data=export_data,
override=False,
project_id=new_project_id,
operator=request.user.username,
)
except Exception as e:
logger.exception("[API] copy common tempalte error: {}".format(e))
logger.exception("The process fails to be replicated across services: {}".format(e))
return {
"result": False,
"message": "invalid flow data or error occur, please contact administrator",
Expand Down
2 changes: 1 addition & 1 deletion gcloud/contrib/template_market/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@


@admin.register(models.TemplateSharedRecord)
class AppMakerAdmin(admin.ModelAdmin):
class TemplateMarketAdmin(admin.ModelAdmin):
list_display = ["project_id", "template_id", "creator", "create_at", "extra_info"]
list_filter = ["project_id", "template_id", "creator", "create_at"]
search_fields = ["project_id", "creator"]
8 changes: 6 additions & 2 deletions 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-06 06:47
# Generated by Django 3.2.15 on 2024-12-09 12:51

from django.db import migrations, models

Expand All @@ -15,7 +15,11 @@ class Migration(migrations.Migration):
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("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")),
("template_id", models.IntegerField(help_text="模版 ID", verbose_name="模版 ID")),
(
"scene_instance_id",
models.CharField(db_index=True, help_text="场景实例 ID", max_length=32, verbose_name="场景实例 ID"),
),
("creator", models.CharField(default="", max_length=32, verbose_name="创建者")),
("create_at", models.DateTimeField(auto_now_add=True, verbose_name="创建时间")),
("extra_info", models.JSONField(blank=True, null=True, verbose_name="额外信息")),
Expand Down
18 changes: 17 additions & 1 deletion gcloud/contrib/template_market/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,27 @@

class TemplateSharedRecord(models.Model):
project_id = models.IntegerField(_("项目 ID"), default=-1, help_text="项目 ID")
template_id = models.IntegerField(_("模版 ID"), db_index=True, help_text="模版 ID")
template_id = models.IntegerField(_("模版 ID"), help_text="模版 ID")
scene_instance_id = models.CharField(_("场景实例 ID"), max_length=32, db_index=True, help_text="场景实例 ID")
creator = models.CharField(_("创建者"), max_length=32, default="")
create_at = models.DateTimeField(_("创建时间"), auto_now_add=True)
extra_info = models.JSONField(_("额外信息"), blank=True, null=True)

class Meta:
verbose_name = _("模板共享记录 TemplateSharedRecord")
verbose_name_plural = _("模板共享记录 TemplateSharedRecord")

@classmethod
def create(cls, project_id, template_id, scene_instance_id, creator="", extra_info=None):
if not scene_instance_id:
raise ValueError("场景实例 ID 不能为空")

instance = cls(
project_id=project_id,
template_id=template_id,
scene_instance_id=scene_instance_id,
creator=creator,
extra_info=extra_info,
)
instance.save()
return instance
40 changes: 40 additions & 0 deletions gcloud/contrib/template_market/permission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
"""
Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community
Edition) available.
Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
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 logging

from rest_framework import permissions

from gcloud.conf import settings
from gcloud.contrib.template_market.models import TemplateSharedRecord


class TemplatePreviewPermission(permissions.BasePermission):
def has_permission(self, request, view):
template_id = request.GET.get("template_id")
project_id = request.GET.get("project_id")

if not template_id or not project_id:
logging.warning("Missing required parameters.")
return False

record = TemplateSharedRecord.objects.filter(template_id=template_id, project_id=project_id).first()
if record is None:
logging.warning("template_id {} does not exist.".format(template_id))
return False

return True


class SharedProcessTemplatePermission(permissions.BasePermission):
def has_permission(self, request, view):
return settings.ENABLE_TEMPLATE_MARKET
46 changes: 43 additions & 3 deletions gcloud/contrib/template_market/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,60 @@
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
from rest_framework import serializers

from gcloud.constants import DATETIME_FORMAT
from gcloud.contrib.template_market.models import TemplateSharedRecord
from gcloud.tasktmpl3.models import TaskTemplate


class TemplatePreviewSerializer(serializers.ModelSerializer):
name = serializers.CharField(read_only=True, help_text="模板名称")
pipeline_tree = serializers.SerializerMethodField(read_only=True, help_text="pipeline_tree")

class TemplateSharedRecordSerializer(serializers.ModelSerializer):
def get_pipeline_tree(self, obj):
return json.dumps(obj.pipeline_tree)

class Meta:
model = TaskTemplate
fields = ["name", "pipeline_tree"]


class TemplateSharedRecordSerializer(serializers.Serializer):
template_id = serializers.CharField(required=True, help_text="模板id")
project_id = serializers.CharField(required=True, help_text="项目id")
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="风险级别")
labels = serializers.ListField(child=serializers.IntegerField(), required=True, help_text="场景标签列表")
usage_content = serializers.CharField(required=True, help_text="使用说明")
creator = serializers.CharField(required=False, max_length=32, help_text="创建者")
create_at = serializers.DateTimeField(required=False, help_text="创建时间", format=DATETIME_FORMAT)
extra_info = serializers.JSONField(required=False, allow_null=True, help_text="额外信息")

def create(self, validated_data):
instance = TemplateSharedRecord.objects.create(
project_id=validated_data["project_id"],
template_id=validated_data["template_id"],
creator=validated_data.get("creator", ""),
extra_info=validated_data.get("extra_info"),
)
return instance

class Meta:
model = TemplateSharedRecord
fields = ["project_id", "template_id", "creator", "create_at", "extra_info"]
fields = [
"project_id",
"template_id",
"creator",
"create_at",
"extra_info",
"name",
"code",
"category",
"risk_level",
"labels",
"usage_content",
]
10 changes: 5 additions & 5 deletions gcloud/contrib/template_market/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@

from django.conf.urls import include, url
from rest_framework.routers import DefaultRouter
from gcloud.contrib.template_market.viewsets import StoreTemplateViewSet, SharedProcessTemplateViewSet
from gcloud.contrib.template_market.viewsets import TemplatePreviewViewSet, SharedProcessTemplateViewSet


drf_api = DefaultRouter()
drf_api.register(r"store_templates", StoreTemplateViewSet)
drf_api.register(r"shared_process_templates", SharedProcessTemplateViewSet)
template_market_router = DefaultRouter()
template_market_router.register(r"template_preview", TemplatePreviewViewSet)
template_market_router.register(r"shared_process_templates", SharedProcessTemplateViewSet)

urlpatterns = [
url(r"^api/", include(drf_api.urls)),
url(r"^api/", include(template_market_router.urls)),
]
Loading

0 comments on commit cbb17a8

Please sign in to comment.