Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: 流程市场共享模板查看 #7626 #7630

7 changes: 7 additions & 0 deletions config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"gcloud.contrib.develop",
"gcloud.contrib.collection",
"gcloud.contrib.operate_record",
"gcloud.contrib.template_market",
"gcloud.apigw",
"gcloud.common_template",
"gcloud.label",
Expand Down Expand Up @@ -883,3 +884,9 @@ def check_engine_admin_permission(request, *args, **kwargs):
if "BKAPP_SOPS_BROKER_URL" in os.environ:
BROKER_URL = os.getenv("BKAPP_SOPS_BROKER_URL")
print(f"BROKER_URL: {BROKER_URL}")


# 流程商店
ENABLE_TEMPLATE_MARKET = env.ENABLE_TEMPLATE_MARKET
# 流程商店 API 地址
TEMPLATE_MARKET_API_URL = env.TEMPLATE_MARKET_API_URL
10 changes: 5 additions & 5 deletions config/urls_custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@
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.
"""
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from django.conf.urls import include, url
from django.conf import settings

from django.conf.urls import include, url
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import permissions

# 用户自定义 urlconf
urlpatterns_custom = [
Expand All @@ -41,6 +40,7 @@
url(r"^plugin_service/", include("plugin_service.urls")),
url(r"^mako_operations/", include("gcloud.mako_template_helper.urls")),
url(r"^engine_admin/", include("pipeline.contrib.engine_admin.urls")),
url(r"^template_market/", include("gcloud.contrib.template_market.urls")),
]

schema_view = get_schema_view(
Expand Down
5 changes: 5 additions & 0 deletions env.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,8 @@
# bk_audit
BK_AUDIT_ENDPOINT = os.getenv("BK_AUDIT_ENDPOINT", None)
BK_AUDIT_DATA_TOKEN = os.getenv("BK_AUDIT_DATA_TOKEN", None)

# 流程商店
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", "")
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: false
allowApplyPermission: false
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: []
2 changes: 2 additions & 0 deletions gcloud/apigw/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +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_project import copy_template_across_project

urlpatterns = [
url(r"^dispatch_plugin_query/$", dispatch_plugin_query),
Expand Down Expand Up @@ -129,4 +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_project/(?P<project_id>\d+)/$", copy_template_across_project),
]
29 changes: 29 additions & 0 deletions gcloud/apigw/validators/copy_template_across_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -*- 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

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

return True, ""
guohelu marked this conversation as resolved.
Show resolved Hide resolved
70 changes: 70 additions & 0 deletions gcloud/apigw/views/copy_template_across_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# -*- 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 ujson as json
guohelu marked this conversation as resolved.
Show resolved Hide resolved
from apigw_manager.apigw.decorators import apigw_require
from blueapps.account.decorators import login_exempt
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST

from gcloud import err_code
from gcloud.apigw.decorators import (
mark_request_whether_is_trust,
project_inject,
return_json_response,
)

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
@csrf_exempt
@require_POST
@apigw_require
@return_json_response
@project_inject
@request_validate(CopyTemplateAcrossProjectValidator)
@mark_request_whether_is_trust
def copy_template_across_project(request, project_id):
if not request.is_trust:
guohelu marked this conversation as resolved.
Show resolved Hide resolved
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["new_project_id"]
template_id = params_data["template_id"]

try:
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("The template fails to be copied across project: {}".format(e))
return {
"result": False,
"message": "invalid flow data or error occur, please contact administrator",
"code": err_code.UNKNOWN_ERROR.code,
}

return format_import_result_to_response_data(import_result)
Empty file.
23 changes: 23 additions & 0 deletions gcloud/contrib/template_market/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- 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.
"""

from django.contrib import admin

from gcloud.contrib.template_market import models


@admin.register(models.TemplateSharedRecord)
class TemplateSharedRecordAdmin(admin.ModelAdmin):
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 = ["market_record_id", "project_id", "creator"]
6 changes: 6 additions & 0 deletions gcloud/contrib/template_market/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class TemplatemakerConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "gcloud.contrib.template_market"
39 changes: 39 additions & 0 deletions gcloud/contrib/template_market/clients.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- 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 requests

from gcloud.conf import settings


class MarketAPIClient:
def __init__(self):
self.base_url = settings.TEMPLATE_MARKET_API_URL

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

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_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_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()
30 changes: 30 additions & 0 deletions gcloud/contrib/template_market/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 3.2.15 on 2024-12-12 09:15

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = []

operations = [
migrations.CreateModel(
name="TemplateSharedRecord",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, 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="创建时间")),
("update_at", models.DateTimeField(auto_now=True, verbose_name="更新时间")),
("extra_info", models.JSONField(blank=True, null=True, verbose_name="额外信息")),
],
options={
"verbose_name": "模板共享记录 TemplateSharedRecord",
"verbose_name_plural": "模板共享记录 TemplateSharedRecord",
},
),
]
Empty file.
29 changes: 29 additions & 0 deletions gcloud/contrib/template_market/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -*- 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.
"""

from django.db import models
from django.utils.translation import ugettext_lazy as _


class TemplateSharedRecord(models.Model):
market_record_id = models.CharField(_("共享实例 ID"), max_length=32, help_text="共享实例 ID", db_index=True)
guohelu marked this conversation as resolved.
Show resolved Hide resolved
project_id = models.IntegerField(_("项目 ID"), default=-1, help_text="项目 ID")
templates = models.JSONField(_("模板 ID 列表"), help_text="模板 ID 列表")
creator = models.CharField(_("创建者"), max_length=32, default="")
create_at = models.DateTimeField(_("创建时间"), auto_now_add=True)
update_at = models.DateTimeField(verbose_name=_("更新时间"), auto_now=True)
extra_info = models.JSONField(_("额外信息"), blank=True, null=True)

class Meta:
verbose_name = _("模板共享记录 TemplateSharedRecord")
verbose_name_plural = _("模板共享记录 TemplateSharedRecord")
59 changes: 59 additions & 0 deletions gcloud/contrib/template_market/permission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# -*- 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 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):
serializer = TemplateProjectBaseSerializer(data=request.GET)
serializer.is_valid(raise_exception=True)

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

return True


class SharedProcessTemplatePermission(permissions.BasePermission):
guohelu marked this conversation as resolved.
Show resolved Hide resolved
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")
guohelu marked this conversation as resolved.
Show resolved Hide resolved
return False

return True
Loading
Loading