diff --git a/.npmignore b/.npmignore index af732845..fb526c75 100644 --- a/.npmignore +++ b/.npmignore @@ -13,3 +13,7 @@ prettier.config.js release.config.js commitlint.config.js .editorconfig +src +*.ts +tsconfig.json +babel.config.js \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f47452da..d33501de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## [2.27.2](https://github.com/serverless-tencent/tencent-component-toolkit/compare/v2.27.1...v2.27.2) (2024-11-19) + + +### Bug Fixes + +* reverse version ([#303](https://github.com/serverless-tencent/tencent-component-toolkit/issues/303)) ([9879308](https://github.com/serverless-tencent/tencent-component-toolkit/commit/9879308b431660b138203f22a72842e211c7118d)) + +## [2.27.1](https://github.com/serverless-tencent/tencent-component-toolkit/compare/v2.27.0...v2.27.1) (2024-11-19) + + +### Bug Fixes + +* support add alisa and publish function version ([#302](https://github.com/serverless-tencent/tencent-component-toolkit/issues/302)) ([1f3780b](https://github.com/serverless-tencent/tencent-component-toolkit/commit/1f3780bb434c841b7e274d8ff4661768ea4821cb)) + # [2.27.0](https://github.com/serverless-tencent/tencent-component-toolkit/compare/v2.26.0...v2.27.0) (2024-11-13) diff --git a/package.json b/package.json index c06135d0..3583236a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tencent-component-toolkit", - "version": "2.27.0", + "version": "2.27.2", "description": "Tencent component toolkit", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -67,6 +67,7 @@ "@semantic-release/npm": "^7.0.4", "@semantic-release/release-notes-generator": "^9.0.1", "@types/axios": "^0.14.0", + "@types/lodash": "^4.17.17", "@types/react-grid-layout": "^1.1.2", "@types/uuid": "^8.3.1", "@typescript-eslint/eslint-plugin": "^4.14.0", @@ -94,6 +95,7 @@ "camelcase": "^6.2.0", "cos-nodejs-sdk-v5": "^2.9.20", "dayjs": "^1.10.4", + "lodash": "^4.17.21", "moment": "^2.29.1", "tencent-cloud-sdk": "^1.0.5", "type-fest": "^0.20.2", diff --git a/src/modules/scf/apis.ts b/src/modules/scf/apis.ts index f0cc8e1a..f0bb4c71 100644 --- a/src/modules/scf/apis.ts +++ b/src/modules/scf/apis.ts @@ -10,6 +10,7 @@ const ACTIONS = [ 'GetFunctionEventInvokeConfig', 'UpdateFunctionEventInvokeConfig', 'CreateTrigger', + 'UpdateTrigger', 'DeleteTrigger', 'PublishVersion', 'ListVersionByFunction', diff --git a/src/modules/scf/config.ts b/src/modules/scf/config.ts index 4f72d16d..13d29bfa 100644 --- a/src/modules/scf/config.ts +++ b/src/modules/scf/config.ts @@ -6,6 +6,7 @@ const CONFIGS = { defaultInitTimeout: 3, waitStatus: ['Creating', 'Updating', 'Publishing', 'Deleting'], failStatus: ['CreateFailed ', 'UpdateFailed', 'PublishFailed', 'DeleteFailed'], + defaultDiskSize: 512, }; export default CONFIGS; diff --git a/src/modules/scf/entities/alias.ts b/src/modules/scf/entities/alias.ts index c1f09ea4..117718af 100644 --- a/src/modules/scf/entities/alias.ts +++ b/src/modules/scf/entities/alias.ts @@ -20,14 +20,17 @@ export default class AliasEntity extends BaseEntity { Name: inputs.aliasName, Namespace: inputs.namespace || 'default', Description: inputs.description || 'Published by Serverless Component', + RoutingConfig: { + AdditionalVersionWeights: inputs.additionalVersions + ? inputs.additionalVersions?.map((v) => { + return { + Version: v.version, + Weight: v.weight, + }; + }) + : [], + }, }; - if (inputs.lastVersion && inputs.traffic) { - publishInputs.RoutingConfig = { - AdditionalVersionWeights: [ - { Version: inputs.lastVersion, Weight: strip(1 - inputs.traffic) }, - ], - }; - } const Response = await this.request(publishInputs); return Response; } @@ -43,12 +46,14 @@ export default class AliasEntity extends BaseEntity { Name: inputs.aliasName || '$DEFAULT', Namespace: inputs.namespace || 'default', RoutingConfig: { - AdditionalVersionWeights: inputs.additionalVersions?.map((v) => { - return { - Version: v.version, - Weight: v.weight, - }; - }), + AdditionalVersionWeights: inputs.additionalVersions + ? inputs.additionalVersions?.map((v) => { + return { + Version: v.version, + Weight: v.weight, + }; + }) + : [], }, Description: inputs.description || 'Configured by Serverless Component', }; diff --git a/src/modules/scf/index.ts b/src/modules/scf/index.ts index 49cfc996..96fe77c5 100644 --- a/src/modules/scf/index.ts +++ b/src/modules/scf/index.ts @@ -280,6 +280,21 @@ export default class Scf { const functionName = inputs.name; const { ignoreTriggers = false } = inputs; + if (inputs?.aliasName) { + if (!inputs?.additionalVersionWeights) { + throw new ApiTypeError( + 'PARAMETER_SCF', + 'additionalVersionWeights is required when aliasName is setted', + ); + } + if (!inputs.publish && !inputs?.aliasFunctionVersion) { + throw new ApiTypeError( + 'PARAMETER_SCF', + 'aliasFunctionVersion is required when aliasName is setted', + ); + } + } + // 在部署前,检查函数初始状态,如果初始为 CreateFailed,尝试先删除,再重新创建 let funcInfo = await this.scf.getInitialStatus({ namespace, functionName }); @@ -311,6 +326,11 @@ export default class Scf { namespace, description: inputs.publishDescription, }); + + if (inputs.aliasName) { + inputs.aliasFunctionVersion = FunctionVersion; + } + inputs.lastVersion = FunctionVersion; outputs.LastVersion = FunctionVersion; @@ -321,13 +341,10 @@ export default class Scf { }); } - const aliasAddionalVersion = inputs.aliasAddionalVersion || inputs.lastVersion; - const needSetTraffic = - inputs.traffic != null && aliasAddionalVersion && aliasAddionalVersion !== '$LATEST'; - const needSetAlias = (inputs.aliasName && inputs.aliasName !== '$DEFAULT') || needSetTraffic; - if (needSetAlias) { + // 检测配置的别名是否存在,不存在就创建,存在的话就设置流量 + if (inputs.aliasName) { let needCreateAlias = false; - if (inputs.aliasName && inputs.aliasName !== '$DEFAULT') { + if (inputs.aliasName !== '$DEFAULT') { try { const aliasInfo = await this.alias.get({ namespace, @@ -347,32 +364,51 @@ export default class Scf { } } } - if (needCreateAlias) { - await this.alias.create({ - namespace, - functionName, - functionVersion: inputs.aliasFunctionVersion || funcInfo?.Qualifier, - aliasName: inputs.aliasName!, - lastVersion: aliasAddionalVersion!, - traffic: inputs.traffic!, - description: inputs.aliasDescription, - }); - } else { + try { + // 创建别名 + if (needCreateAlias) { + await this.alias.create({ + namespace, + functionName, + functionVersion: inputs.aliasFunctionVersion || funcInfo?.Qualifier, + aliasName: inputs.aliasName!, + description: inputs.aliasDescription, + additionalVersions: inputs.additionalVersionWeights, + }); + } else { + // 更新别名 + await this.alias.update({ + namespace, + functionName, + functionVersion: inputs.aliasFunctionVersion || funcInfo?.Qualifier, + additionalVersions: inputs.additionalVersionWeights, + region: this.region, + aliasName: inputs.aliasName, + description: inputs.aliasDescription, + }); + } + } catch (error) { + const errorType = needCreateAlias ? 'CREATE_ALIAS_SCF' : 'UPDATE_ALIAS_SCF'; + throw new ApiTypeError(errorType, error.message); + } + } else { + // 兼容旧逻辑,即给默认版本$LATEST设置traffic比例的流量,给lastVersion版本设置(1-traffic)比例的流量。 + const needSetTraffic = + inputs.traffic != null && inputs.lastVersion && inputs.lastVersion !== '$LATEST'; + if (needSetTraffic) { await this.alias.update({ namespace, functionName, - functionVersion: inputs.aliasFunctionVersion || funcInfo?.Qualifier, + region: this.region, additionalVersions: needSetTraffic - ? [{ weight: strip(1 - inputs.traffic!), version: aliasAddionalVersion! }] + ? [{ weight: strip(1 - inputs.traffic!), version: inputs.lastVersion! }] : [], - region: this.region, aliasName: inputs.aliasName, description: inputs.aliasDescription, }); + outputs.Traffic = inputs.traffic; + outputs.ConfigTrafficVersion = inputs.lastVersion; } - - outputs.Traffic = inputs.traffic; - outputs.ConfigTrafficVersion = inputs.lastVersion; } // get default alias diff --git a/src/modules/scf/interface.ts b/src/modules/scf/interface.ts index 9825ae3d..f86086e7 100644 --- a/src/modules/scf/interface.ts +++ b/src/modules/scf/interface.ts @@ -35,6 +35,7 @@ export interface BaseFunctionConfig { Timeout?: number; InitTimeout?: number; MemorySize?: number; + DiskSize?: number; Type?: 'HTTP' | 'Event'; DeployMode?: 'code' | 'image'; PublicNetConfig?: { @@ -69,6 +70,7 @@ export interface BaseFunctionConfig { ProtocolParams?: ProtocolParams; NodeType?: string; NodeSpec?: string; + InstanceConcurrencyConfig?: { DynamicEnabled: 'TRUE' | 'FALSE'; MaxConcurrency?: number }; } export interface TriggerType { @@ -145,9 +147,10 @@ export interface ScfCreateAlias { functionVersion?: string; aliasName: string; namespace?: string; - lastVersion: string; - traffic: number; + lastVersion?: string; + traffic?: number; description?: string; + additionalVersions?: { version: string; weight: number }[]; } export interface ScfCreateFunctionInputs { @@ -167,6 +170,7 @@ export interface ScfCreateFunctionInputs { timeout?: number; initTimeout?: number; memorySize?: number; + diskSize?: number; publicAccess?: boolean; eip?: boolean; l5Enable?: boolean; @@ -245,6 +249,13 @@ export interface ScfCreateFunctionInputs { protocolType?: string; protocolParams?: ProtocolParams; + + // 请求多并发配置 + instanceConcurrencyConfig?: { + enable: boolean; // 是否开启多并发 + dynamicEnabled: boolean; // 是否开启动态配置 + maxConcurrency: number; // 最大并发数 + }; } export interface ScfUpdateAliasTrafficInputs { @@ -270,17 +281,18 @@ export interface ScfDeployInputs extends ScfCreateFunctionInputs { enableRoleAuth?: boolean; region?: string; + // 版本相关配置 lastVersion?: string; publish?: boolean; publishDescription?: string; - needSetTraffic?: boolean; traffic?: number; + // 别名相关配置 aliasName?: string; aliasDescription?: string; aliasFunctionVersion?: string; - aliasAddionalVersion?: string; + additionalVersionWeights?: { version: string; weight: number }[]; tags?: Record; diff --git a/src/modules/scf/utils.ts b/src/modules/scf/utils.ts index a7da9fa3..c981250a 100644 --- a/src/modules/scf/utils.ts +++ b/src/modules/scf/utils.ts @@ -21,6 +21,7 @@ export const formatInputs = (inputs: ScfCreateFunctionInputs) => { }, L5Enable: inputs.l5Enable === true ? 'TRUE' : 'FALSE', InstallDependency: inputs.installDependency === true ? 'TRUE' : 'FALSE', + DiskSize: +(inputs.diskSize || CONFIGS.defaultDiskSize), }; if (inputs.nodeType) { @@ -96,6 +97,19 @@ export const formatInputs = (inputs: ScfCreateFunctionInputs) => { functionInputs.ProtocolParams = protocolParams; } } + // 仅web函数支持单实例请求多并发,instanceConcurrencyConfig.enable:true,启用多并发;instanceConcurrencyConfig.enable:false,关闭多并发 + if (inputs.instanceConcurrencyConfig) { + if (inputs.instanceConcurrencyConfig.enable) { + functionInputs.InstanceConcurrencyConfig = { + DynamicEnabled: inputs.instanceConcurrencyConfig.dynamicEnabled ? 'TRUE' : 'FALSE', + MaxConcurrency: inputs.instanceConcurrencyConfig.maxConcurrency || 2, + }; + } else { + functionInputs.InstanceConcurrencyConfig = { + DynamicEnabled: '' as any, + }; + } + } } if (inputs.role) { diff --git a/src/modules/triggers/base.ts b/src/modules/triggers/base.ts index c7182a2a..150a06cb 100644 --- a/src/modules/triggers/base.ts +++ b/src/modules/triggers/base.ts @@ -116,4 +116,4 @@ export const TRIGGER_STATUS_MAP = { 0: 'CLOSE', }; -export const CAN_UPDATE_TRIGGER = ['apigw', 'cls', 'mps', 'clb']; +export const CAN_UPDATE_TRIGGER = ['apigw', 'cls', 'mps', 'clb','http','ckafka']; \ No newline at end of file diff --git a/src/modules/triggers/ckafka.ts b/src/modules/triggers/ckafka.ts index 652eb894..d25f8498 100644 --- a/src/modules/triggers/ckafka.ts +++ b/src/modules/triggers/ckafka.ts @@ -1,8 +1,9 @@ import { CapiCredentials, RegionType } from './../interface'; -import { TriggerInputs, CkafkaTriggerInputsParams, CreateTriggerReq } from './interface'; +import { TriggerInputs, CkafkaTriggerInputsParams, CreateTriggerReq,TriggerAction } from './interface'; import Scf from '../scf'; -import { TRIGGER_STATUS_MAP } from './base'; +import { TRIGGER_STATUS_MAP } from './base'; import { TriggerManager } from './manager'; +import { getScfTriggerByName } from './utils'; export default class CkafkaTrigger { credentials: CapiCredentials; @@ -29,20 +30,22 @@ export default class CkafkaTrigger { return `${triggerInputs.Type}-${triggerInputs.TriggerName}-${desc}-${Enable}-${triggerInputs.Qualifier}`; } - formatInputs({ inputs }: { inputs: TriggerInputs }) { + formatInputs({ inputs,action = 'CreateTrigger'}: { inputs: TriggerInputs,action?: TriggerAction }) { const { parameters } = inputs; + const triggerName = parameters?.name || `${parameters?.instanceId}-${parameters?.topic}`; const triggerInputs: CreateTriggerReq = { - Action: 'CreateTrigger', + Action: action, FunctionName: inputs.functionName, Namespace: inputs.namespace, Type: 'ckafka', Qualifier: parameters?.qualifier ?? '$DEFAULT', - TriggerName: `${parameters?.name}-${parameters?.topic}`, + TriggerName: triggerName, TriggerDesc: JSON.stringify({ maxMsgNum: parameters?.maxMsgNum ?? 100, offset: parameters?.offset ?? 'latest', retry: parameters?.retry ?? 10000, timeOut: parameters?.timeout ?? 60, + consumerGroupName: parameters?.consumerGroupName ?? '', }), Enable: parameters?.enable ? 'OPEN' : 'CLOSE', }; @@ -57,16 +60,35 @@ export default class CkafkaTrigger { async create({ scf, inputs, + region }: { scf: Scf | TriggerManager; region: RegionType; inputs: TriggerInputs; }) { - const { triggerInputs } = this.formatInputs({ inputs }); - console.log(`Creating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`); - const { TriggerInfo } = await scf.request(triggerInputs as any); - TriggerInfo.Qualifier = TriggerInfo.Qualifier || triggerInputs.Qualifier; - return TriggerInfo; + // 查询当前触发器是否已存在 + const existTrigger = await getScfTriggerByName({ scf, region, inputs }); + // 更新触发器 + if (existTrigger) { + const { triggerInputs } = this.formatInputs({ inputs, action: 'UpdateTrigger' }); + console.log(`${triggerInputs.Type} trigger ${triggerInputs.TriggerName} is exist`) + console.log(`Updating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`); + try { + // 更新触发器 + await scf.request(triggerInputs as any); + // 更新成功后,查询最新的触发器信息 + const trigger = await getScfTriggerByName({ scf, region, inputs }); + return trigger; + } catch (error) { + return {} + } + } else { // 创建触发器 + const { triggerInputs } = this.formatInputs({ inputs }); + console.log(`Creating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`); + const { TriggerInfo } = await scf.request(triggerInputs as any); + TriggerInfo.Qualifier = TriggerInfo.Qualifier || triggerInputs.Qualifier; + return TriggerInfo; + } } async delete({ scf, inputs }: { scf: Scf; inputs: TriggerInputs }) { console.log(`Removing ${inputs.type} trigger ${inputs.triggerName}`); diff --git a/src/modules/triggers/http.ts b/src/modules/triggers/http.ts index dc20dfbc..f5e26f0f 100644 --- a/src/modules/triggers/http.ts +++ b/src/modules/triggers/http.ts @@ -2,7 +2,9 @@ import Scf from '../scf'; import { TriggerManager } from './manager'; import { CapiCredentials, RegionType } from './../interface'; import BaseTrigger from './base'; -import { HttpTriggerInputsParams, TriggerInputs, CreateTriggerReq } from './interface'; +import { HttpTriggerInputsParams, TriggerInputs, CreateTriggerReq,TriggerAction } from './interface'; +import { caseForObject } from '../../utils'; +import { getScfTriggerByName } from './utils'; export default class HttpTrigger extends BaseTrigger { credentials: CapiCredentials; @@ -15,31 +17,33 @@ export default class HttpTrigger extends BaseTrigger { } getKey(triggerInputs: CreateTriggerReq) { - const triggerDesc = JSON.parse(triggerInputs.TriggerDesc!); - const tempDest = JSON.stringify({ - authType: triggerDesc?.AuthType, - enableIntranet: triggerDesc?.NetConfig?.EnableIntranet, - enableExtranet: triggerDesc?.NetConfig?.EnableExtranet, - }); - return `http-${tempDest}-${triggerInputs.Qualifier}`; + return `http-${triggerInputs?.TriggerName}`; } - formatInputs({ inputs }: { region: RegionType; inputs: TriggerInputs }) { + formatInputs({ inputs,action = 'CreateTrigger' }: { region: RegionType; inputs: TriggerInputs ,action?: TriggerAction}) { const { parameters } = inputs; + const triggerName = parameters?.name || 'url-trigger'; + const { origins,headers,methods,exposeHeaders } = parameters?.corsConfig || {} const triggerInputs: CreateTriggerReq = { - Action: 'CreateTrigger', + Action: action, FunctionName: inputs.functionName, Namespace: inputs.namespace, - Type: 'http', Qualifier: parameters?.qualifier || '$DEFAULT', - TriggerName: parameters?.name || 'url-trigger', + TriggerName: triggerName, TriggerDesc: JSON.stringify({ AuthType: parameters?.authType || 'NONE', NetConfig: { EnableIntranet: parameters?.netConfig?.enableIntranet ?? false, EnableExtranet: parameters?.netConfig?.enableExtranet ?? false, }, + CorsConfig: parameters?.corsConfig ? caseForObject({ + ...parameters?.corsConfig, + origins: typeof origins === 'string' ? origins?.split(',') : origins, + methods: typeof methods === 'string' ? methods?.split(',') : methods, + headers: typeof headers === 'string' ? headers?.split(',') : headers, + exposeHeaders: typeof exposeHeaders === 'string' ? exposeHeaders?.split(',') : exposeHeaders, + },'upper') : undefined }), Enable: 'OPEN', }; @@ -61,12 +65,29 @@ export default class HttpTrigger extends BaseTrigger { region: RegionType; inputs: TriggerInputs; }) { - const { triggerInputs } = this.formatInputs({ region, inputs }); - console.log(`Creating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`); - const { TriggerInfo } = await scf.request(triggerInputs); - TriggerInfo.Qualifier = TriggerInfo.Qualifier || triggerInputs.Qualifier; - - return TriggerInfo; + // 查询当前触发器是否已存在 + const existTrigger = await getScfTriggerByName({ scf, region, inputs }); + // 更新触发器 + if (existTrigger) { + const { triggerInputs } = this.formatInputs({ region, inputs, action: 'UpdateTrigger' }); + console.log(`${triggerInputs.Type} trigger ${triggerInputs.TriggerName} is exist`) + console.log(`Updating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`); + try { + // 更新触发器 + await scf.request(triggerInputs); + // 更新成功后,查询最新的触发器信息 + const trigger = await getScfTriggerByName({ scf, region, inputs }); + return trigger; + } catch (error) { + return {} + } + } else { // 创建触发器 + const { triggerInputs } = this.formatInputs({ region, inputs }); + console.log(`Creating ${triggerInputs.Type} trigger ${triggerInputs.TriggerName}`); + const { TriggerInfo } = await scf.request(triggerInputs); + TriggerInfo.Qualifier = TriggerInfo.Qualifier || triggerInputs.Qualifier; + return TriggerInfo; + } } async delete({ diff --git a/src/modules/triggers/interface/index.ts b/src/modules/triggers/interface/index.ts index 3717e084..20ce7e07 100644 --- a/src/modules/triggers/interface/index.ts +++ b/src/modules/triggers/interface/index.ts @@ -1,6 +1,7 @@ import { ApigwDeployInputs, ApiEndpoint } from '../../apigw/interface'; import { TagInput } from '../../interface'; +export type TriggerAction = 'CreateTrigger' | 'UpdateTrigger' export interface ApigwTriggerRemoveScfTriggerInputs { serviceId: string; apiId: string; @@ -42,7 +43,7 @@ export interface ApigwTriggerInputsParams extends ApigwDeployInputs { export type TriggerType = 'scf' | 'timer' | string; export interface CreateTriggerReq { - Action?: 'CreateTrigger'; + Action?: TriggerAction; ResourceId?: string; FunctionName?: string; Namespace?: string; @@ -57,11 +58,13 @@ export interface CreateTriggerReq { export interface CkafkaTriggerInputsParams extends TriggerInputsParams { qualifier?: string; name?: string; - topic?: string; + instanceId?: string; //ckafka实例ID + topic?: string; //ckafka主题名称 maxMsgNum?: number; offset?: number; retry?: number; timeout?: number; + consumerGroupName?: string; enable?: boolean; } @@ -100,6 +103,15 @@ export interface HttpTriggerInputsParams { enableIntranet?: boolean; enableExtranet?: boolean; }; + corsConfig: { + enable: boolean + origins: Array | string + methods: Array | string + headers: Array | string + exposeHeaders: Array | string + credentials: boolean + maxAge: number + } } export interface MpsTriggerInputsParams { @@ -120,6 +132,7 @@ export interface TimerTriggerInputsParams { export interface TriggerInputs

{ functionName: string; + Type?: string; // 兼容scf组件触发器类型字段 type?: string; triggerDesc?: string; triggerName?: string; diff --git a/src/modules/triggers/manager.ts b/src/modules/triggers/manager.ts index 18f6d170..e62f77d1 100644 --- a/src/modules/triggers/manager.ts +++ b/src/modules/triggers/manager.ts @@ -277,11 +277,17 @@ export class TriggerManager { // 1. 删除老的无法更新的触发器 for (let i = 0, len = deleteList.length; i < len; i++) { const trigger = deleteList[i]; - await this.removeTrigger({ - name, - namespace, - trigger, - }); + // 若类型触发器不支持编辑,需要先删除,后重新创建; + if (!CAN_UPDATE_TRIGGER.includes(trigger?.Type)) { + await this.removeTrigger({ + name, + namespace, + trigger, + }); + } else { + // 若触发器类型支持编辑,直接跳过删除 + continue; + } } // 2. 创建新的触发器 diff --git a/src/modules/triggers/utils/index.ts b/src/modules/triggers/utils/index.ts new file mode 100644 index 00000000..5b3e1e8a --- /dev/null +++ b/src/modules/triggers/utils/index.ts @@ -0,0 +1,41 @@ +import { RegionType } from "../../interface"; +import Scf from "../../scf"; +import { CkafkaTriggerInputsParams, HttpTriggerInputsParams, TriggerDetail, TriggerInputs } from "../interface"; +import { TriggerManager } from "../manager"; + +// 获取函数下指定类型以及指定触发器名称的触发器 +export async function getScfTriggerByName({ + scf, + inputs + }: { + scf: Scf | TriggerManager; + region: RegionType; + inputs: TriggerInputs; + }): Promise { + const filters = [ + { + Name: 'Type', + Values: [inputs?.type || inputs?.Type] + } + ] + if (inputs?.parameters?.name) { + filters.push({ + Name: 'TriggerName', + Values: [inputs?.parameters?.name] + }) + } + if (inputs?.parameters?.qualifier) { + filters.push({ + Name: 'Qualifier', + Values: [inputs?.parameters?.qualifier?.toString()] + }) + } + const response = await scf.request({ + Action: 'ListTriggers', + FunctionName: inputs?.functionName, + Namespace: inputs?.namespace, + Limit: 1000, + Filters: filters + }); + return response?.Triggers?.[0]; +} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index 7677a34a..8446dc19 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,6 +4,8 @@ import camelCase from 'camelcase'; import { PascalCase } from 'type-fest'; import { CamelCasedProps, PascalCasedProps } from '../modules/interface'; import crypto from 'crypto'; +import _ from 'lodash'; + // TODO: 将一些库换成 lodash @@ -31,6 +33,22 @@ export function isArray(obj: T[] | T): obj is T[] { return Object.prototype.toString.call(obj) == '[object Array]'; } +/** + * is positive integer(正整数) + * @param obj object + */ +export function isPositiveInteger(value: string | number) { + return +value > 0 && Number.isInteger(+value); +} + +/** + * is number(数字) + * @param obj object + */ +export function isNumber(value: string | number) { + return !Number.isNaN(+value); +} + /** * is object * @param obj object @@ -298,3 +316,24 @@ export const getYunTiApiUrl = (): string => { const url = `${apiUrl}?api_key=${apiKey}&api_ts=${timeStamp}&api_sign=${apiSign}`; return url; }; + + + +/** + * 首字母转换大小写 + * @param {*} obj + * @param {*} type + * @returns + */ +export function caseForObject(obj: object,type : 'upper' | 'lower') { + if (!_.isPlainObject(obj)) return obj; + return _.transform(obj, (result: { [key: string]: any }, value, key) => { + let newKey:string = ''; + if (type === 'upper') { + newKey = _.upperFirst(key) + } else { + newKey = _.lowerFirst(key); + } + result[newKey] = _.isPlainObject(value) ? caseForObject(value,type) : value; + }, {}); +} \ No newline at end of file