From 967846bef590b76d27e472180b96372ff55cfb6f Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sun, 26 Jul 2026 19:01:18 +0800 Subject: [PATCH] =?UTF-8?q?perf(tencent-clb):=20=E4=B8=BA=E8=85=BE?= =?UTF-8?q?=E8=AE=AF=E4=BA=91CLB=E9=83=A8=E7=BD=B2=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=BF=9C=E7=A8=8B=E9=80=89=E6=8B=A9=E8=BE=93?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构插件输入配置,使用createRemoteSelectInputDefine统一封装远程选择组件 - 新增onGetCLBList、onGetListenerList、onGetDomainList方法实现数据拉取和格式转换 - 补全完整的单元测试用例,覆盖输入配置校验和数据映射逻辑 --- .../plugin/deploy-to-clb/index.test.ts | 111 +++++++++++++++ .../plugin/deploy-to-clb/index.ts | 128 +++++++++++++++--- 2 files changed, 219 insertions(+), 20 deletions(-) create mode 100644 packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.test.ts diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.test.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.test.ts new file mode 100644 index 000000000..1f5086fc4 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.test.ts @@ -0,0 +1,111 @@ +/// + +import assert from "node:assert/strict"; +import { DeployCertToTencentCLB } from "./index.js"; + +describe("DeployCertToTencentCLB", () => { + it("uses remote single-select inputs for CLB and HTTPS listener", () => { + const input = (DeployCertToTencentCLB as any).define.input; + + assert.equal(input.loadBalancerId.component.name, "remote-select"); + assert.equal(input.loadBalancerId.component.single, true); + assert.equal(input.loadBalancerId.component.action, "onGetCLBList"); + assert.equal(input.loadBalancerId.required, true); + + assert.equal(input.listenerId.component.name, "remote-select"); + assert.equal(input.listenerId.component.single, true); + assert.equal(input.listenerId.component.action, "onGetListenerList"); + assert.deepEqual(input.listenerId.component.watches, ["certDomains", "accessId", "region", "loadBalancerId"]); + assert.equal(input.listenerId.required, true); + + assert.equal(input.domain.component.name, "remote-select"); + assert.equal(input.domain.component.single, false); + assert.equal(input.domain.component.action, "onGetDomainList"); + assert.deepEqual(input.domain.component.watches, ["certDomains", "accessId", "region", "loadBalancerId", "listenerId"]); + assert.equal(input.domain.required, false); + }); + + it("maps CLB API results to remote-select options", async () => { + const plugin = new DeployCertToTencentCLB(); + plugin.accessId = "access-1"; + plugin.logger = { info: () => undefined } as any; + (plugin as any).getClient = async () => ({}); + (plugin as any).getCLBList = async () => [ + { + LoadBalancerId: "lb-1", + LoadBalancerName: "业务负载均衡", + }, + ]; + + const options = await plugin.onGetCLBList({}); + + assert.deepEqual(options, [ + { + value: "lb-1", + label: "业务负载均衡", + }, + ]); + }); + + it("maps HTTPS listener API results after selecting a CLB", async () => { + const plugin = new DeployCertToTencentCLB(); + plugin.accessId = "access-1"; + plugin.loadBalancerId = "lb-1"; + plugin.logger = { info: () => undefined } as any; + (plugin as any).getClient = async () => ({}); + (plugin as any).getListenerList = async (_client: any, loadBalancerId: string, listenerIds: any) => { + assert.equal(loadBalancerId, "lb-1"); + assert.equal(listenerIds, null); + return [ + { + ListenerId: "listener-1", + ListenerName: "HTTPS监听器", + Port: 443, + }, + ]; + }; + + const options = await plugin.onGetListenerList({}); + + assert.deepEqual(options, [ + { + value: "listener-1", + label: "HTTPS监听器:443", + }, + ]); + }); + + it("maps SNI domains from the selected listener rules", async () => { + const plugin = new DeployCertToTencentCLB(); + plugin.accessId = "access-1"; + plugin.loadBalancerId = "lb-1"; + plugin.listenerId = "listener-1"; + plugin.logger = { info: () => undefined } as any; + (plugin as any).getClient = async () => ({}); + (plugin as any).getListenerList = async (_client: any, loadBalancerId: string, listenerIds: string[]) => { + assert.equal(loadBalancerId, "lb-1"); + assert.deepEqual(listenerIds, ["listener-1"]); + return [ + { + ListenerId: "listener-1", + Rules: [{ Domain: "www.example.com" }, { Domain: "api.example.com" }], + }, + ]; + }; + + const options = await plugin.onGetDomainList({}); + + assert.deepEqual(options, [ + { + value: "www.example.com", + label: "www.example.com", + domain: "www.example.com", + }, + { + value: "api.example.com", + label: "api.example.com", + domain: "api.example.com", + }, + ]); + }); +}); diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts index 5a49d739d..4737e69b9 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts @@ -1,7 +1,8 @@ -import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; +import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; import dayjs from "dayjs"; import { TencentAccess } from "../../../plugin-lib/tencent/index.js"; import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert"; +import { createRemoteSelectInputDefine } from "@certd/plugin-lib"; @IsTaskPlugin({ name: "DeployCertToTencentCLB", title: "腾讯云-部署到CLB", @@ -73,29 +74,44 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin { }) region!: string; - @TaskInput({ - title: "负载均衡ID", - required: true, - }) + @TaskInput( + createRemoteSelectInputDefine({ + title: "负载均衡ID", + helper: "请选择要部署证书的负载均衡", + action: DeployCertToTencentCLB.prototype.onGetCLBList.name, + watches: ["region"], + single: true, + pager: false, + search: false, + }) + ) loadBalancerId!: string; - @TaskInput({ - title: "监听器ID", - required: true, - }) + @TaskInput( + createRemoteSelectInputDefine({ + title: "监听器ID", + helper: "请选择要部署证书的HTTPS监听器", + action: DeployCertToTencentCLB.prototype.onGetListenerList.name, + watches: ["region", "loadBalancerId"], + single: true, + pager: false, + search: false, + }) + ) listenerId!: string; - @TaskInput({ - title: "域名", - required: false, - component: { - name: "a-select", - vModel: "value", - open: false, - mode: "tags", - }, - helper: "如果开启了sni,则此项必须填写,未开启,则不要填写", - }) + @TaskInput( + createRemoteSelectInputDefine({ + title: "域名", + helper: "如果开启了SNI,请选择要部署证书的域名;未开启SNI时可以留空", + action: DeployCertToTencentCLB.prototype.onGetDomainList.name, + watches: ["region", "loadBalancerId", "listenerId"], + required: false, + single: false, + pager: false, + search: false, + }) + ) domain!: string | string[]; @TaskInput({ @@ -272,6 +288,27 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin { return ret.LoadBalancerSet; } + async onGetCLBList(data: PageSearch) { + if (!this.accessId) { + throw new Error("请选择Access提供者"); + } + + const client = await this.getClient(); + const list = await this.getCLBList(client); + if (!list || list.length === 0) { + return []; + } + + return list.map((item: any) => { + const loadBalancerId = item.LoadBalancerId; + const loadBalancerName = item.LoadBalancerName || loadBalancerId; + return { + value: loadBalancerId, + label: `${loadBalancerName}<${loadBalancerId}>`, + }; + }); + } + async getListenerList(client: any, balancerId: any, listenerIds: any) { // HTTPS const params = { @@ -284,6 +321,57 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin { return ret.Listeners; } + async onGetListenerList(data: PageSearch) { + if (!this.accessId) { + throw new Error("请选择Access提供者"); + } + if (!this.loadBalancerId) { + throw new Error("请先选择负载均衡"); + } + + const client = await this.getClient(); + const list = await this.getListenerList(client, this.loadBalancerId, null); + if (!list || list.length === 0) { + return []; + } + + return list.map((item: any) => { + const listenerId = item.ListenerId; + const listenerName = item.ListenerName || "HTTPS监听器"; + const port = item.Port ? `:${item.Port}` : ""; + return { + value: listenerId, + label: `${listenerName}${port}<${listenerId}>`, + }; + }); + } + + async onGetDomainList(data: PageSearch) { + if (!this.accessId) { + throw new Error("请选择Access提供者"); + } + if (!this.loadBalancerId) { + throw new Error("请先选择负载均衡"); + } + if (!this.listenerId) { + throw new Error("请先选择监听器"); + } + + const client = await this.getClient(); + const listeners = await this.getListenerList(client, this.loadBalancerId, [this.listenerId]); + const listener = listeners?.[0]; + const domains = listener?.Rules?.map((rule: any) => rule.Domain).filter(Boolean) || []; + const uniqueDomains = [...new Set(domains)]; + + return uniqueDomains.map(domain => { + return { + value: domain, + label: domain, + domain, + }; + }); + } + checkRet(ret: any) { if (!ret || ret.Error) { throw new Error("执行失败:" + ret.Error.Code + "," + ret.Error.Message);