Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(scf): support http type
  • Loading branch information
yugasun committed Jun 7, 2021
commit cd5378575e235bbd8fcb4283ddaec33d92582716
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ lib
env.js
package-lock.json
yarn.lock

__tests__/apigw.list.test.ts
47 changes: 24 additions & 23 deletions __tests__/scf.http.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { sleep } from '@ygkit/request';
import { ScfDeployInputs } from '../src/modules/scf/interface';
import { Scf } from '../src';

describe('Scf - special', () => {
describe('Scf - http', () => {
const credentials = {
SecretId: process.env.TENCENT_SECRET_ID,
SecretKey: process.env.TENCENT_SECRET_KEY,
Expand All @@ -19,7 +20,7 @@ describe('Scf - special', () => {
path: '/',
method: 'ANY',
function: {
isIntegratedResponse: true,
type: 'web',
},
},
],
Expand All @@ -31,14 +32,14 @@ describe('Scf - special', () => {
const events = Object.entries(triggers).map(([, value]) => value);

const inputs: ScfDeployInputs = {
name: `serverless-test-http-${Date.now()}`,
// name: `serverless-test-http-${Date.now()}`,
name: `serverless-test-http`,
code: {
bucket: 'test-chongqing',
object: 'express_http.zip',
},
type: 'web',
namespace: 'default',
// role: 'SCF_QcsRole',
runtime: 'Nodejs12.16',
region: 'ap-chongqing',
description: 'Created by Serverless',
Expand All @@ -57,7 +58,7 @@ describe('Scf - special', () => {

let outputs;

test('should deploy SCF success', async () => {
test('deploy', async () => {
outputs = await scf.deploy(inputs);
expect(outputs.FunctionName).toBe(inputs.name);
expect(outputs.Type).toBe('HTTP');
Expand All @@ -67,22 +68,22 @@ describe('Scf - special', () => {
expect(outputs.MemorySize).toBe(inputs.memorySize);
expect(outputs.Runtime).toBe(inputs.runtime);
});
// test('should update SCF success', async () => {
// await sleep(3000);
// outputs = await scf.deploy(inputs);
// expect(outputs.FunctionName).toBe(inputs.name);
// expect(outputs.Type).toBe('HTTP');
// expect(outputs.Qualifier).toBe('$LATEST');
// expect(outputs.Description).toBe('Created by Serverless');
// expect(outputs.Timeout).toBe(inputs.timeout);
// expect(outputs.MemorySize).toBe(inputs.memorySize);
// expect(outputs.Runtime).toBe(inputs.runtime);
// });
// test('should remove Scf success', async () => {
// const res = await scf.remove({
// functionName: inputs.name,
// ...outputs,
// });
// expect(res).toEqual(true);
// });
test('update', async () => {
await sleep(3000);
outputs = await scf.deploy(inputs);
expect(outputs.FunctionName).toBe(inputs.name);
expect(outputs.Type).toBe('HTTP');
expect(outputs.Qualifier).toBe('$LATEST');
expect(outputs.Description).toBe('Created by Serverless');
expect(outputs.Timeout).toBe(inputs.timeout);
expect(outputs.MemorySize).toBe(inputs.memorySize);
expect(outputs.Runtime).toBe(inputs.runtime);
});
test('remove', async () => {
const res = await scf.remove({
functionName: inputs.name,
...outputs,
});
expect(res).toEqual(true);
});
});
2 changes: 2 additions & 0 deletions src/modules/apigw/apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const ACTIONS = [
'DescribeServiceSubDomainMappings',
'BindSubDomain',
'UnBindSubDomain',
'DescribeServicesStatus',
'DescribeServiceEnvironmentList',
] as const;

export type ActionType = typeof ACTIONS[number];
Expand Down
1 change: 1 addition & 0 deletions src/modules/apigw/entities/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ export default class ApiEntity {
);
}
apiInputs.serviceScfFunctionName = endpoint.function.functionName;
apiInputs.serviceScfFunctionType = endpoint.function.functionType;
apiInputs.serviceScfFunctionNamespace = endpoint.function.functionNamespace || 'default';
apiInputs.serviceScfIsIntegratedResponse = endpoint.function.isIntegratedResponse
? true
Expand Down
128 changes: 128 additions & 0 deletions src/modules/apigw/entities/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,28 @@ export default class ServiceEntity {
return result as never;
}

/**
* 获取 API 网关列表
* @param options 参数
* @returns 网关列表
*/
async list(options?: { offset?: number; limit?: number }) {
options = {
...{ limit: 10, offset: 0 },
...(options || {}),
};
try {
const res: { TotalCount: number; ServiceSet: any[] } = await this.request({
Action: 'DescribeServicesStatus',
Offset: options.offset,
Limit: options.limit,
});
return res.ServiceSet || [];
} catch (e) {
return [];
}
}

async getById(serviceId: string) {
try {
const detail: Detail = await this.request({
Expand All @@ -52,6 +74,112 @@ export default class ServiceEntity {
}
}

async removeApiUsagePlan(ServiceId: string) {
const { ApiUsagePlanList = [] } = await this.request({
Action: 'DescribeApiUsagePlan',
ServiceId,
});

for (let i = 0; i < ApiUsagePlanList.length; i++) {
const { UsagePlanId, Environment, ApiId } = ApiUsagePlanList[i];
console.log(`APIGW - Removing api usage plan: ${UsagePlanId}`);
const { AccessKeyList = [] } = await this.request({
Action: 'DescribeUsagePlanSecretIds',
UsagePlanId: UsagePlanId,
Limit: 100,
});

const AccessKeyIds = AccessKeyList.map((item: { SecretId: string }) => item.SecretId);

if (AccessKeyIds && AccessKeyIds.length > 0) {
await this.request({
Action: 'UnBindSecretIds',
UsagePlanId: UsagePlanId,
AccessKeyIds: AccessKeyIds,
});
// delelet all created api key
for (let sIdx = 0; sIdx < AccessKeyIds.length; sIdx++) {
await this.request({
Action: 'DisableApiKey',
AccessKeyId: AccessKeyIds[sIdx],
});
}
}

// unbind environment
await this.request({
Action: 'UnBindEnvironment',
ServiceId,
UsagePlanIds: [UsagePlanId],
Environment: Environment,
BindType: 'API',
ApiIds: [ApiId],
});

await this.request({
Action: 'DeleteUsagePlan',
UsagePlanId: UsagePlanId,
});
}
}

async removeById(serviceId: string) {
try {
const { ApiIdStatusSet = [] } = await this.request({
Action: 'DescribeApisStatus',
ServiceId: serviceId,
Limit: 100,
});

// remove all apis
for (let i = 0; i < ApiIdStatusSet.length; i++) {
const { ApiId } = ApiIdStatusSet[i];

await this.removeApiUsagePlan(serviceId);

console.log(`APIGW - Removing api: ${ApiId}`);

await this.request({
Action: 'DeleteApi',
ServiceId: serviceId,
ApiId,
});
}

// unrelease service
// get environment list
const { EnvironmentList = [] } = await this.request({
Action: 'DescribeServiceEnvironmentList',
ServiceId: serviceId,
});

for (let i = 0; i < EnvironmentList.length; i++) {
const { EnvironmentName, Status } = EnvironmentList[i];
if (Status === 1) {
try {
console.log(
`APIGW - Unreleasing service: ${serviceId}, environment: ${EnvironmentName}`,
);
await this.request({
Action: 'UnReleaseService',
ServiceId: serviceId,
EnvironmentName,
});
} catch (e) {}
}
}

// delete service
console.log(`APIGW - Removing service: ${serviceId}`);
await this.request({
Action: 'DeleteService',
ServiceId: serviceId,
});
} catch (e) {
console.error(e);
}
}

/** 创建 API 网关服务 */
async create(serviceConf: ApigwCreateServiceInputs): Promise<ApigwCreateOrUpdateServiceOutputs> {
const {
Expand Down
1 change: 1 addition & 0 deletions src/modules/scf/entities/scf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ export default class ScfEntity extends BaseEntity {
delete reqInputs.CodeSource;
delete reqInputs.AsyncRunEnable;
delete reqInputs.InstallDependency;
delete reqInputs.DeployMode;

// +++++++++++++++++++++++
// FIXME: 以下是函数绑定层逻辑,当函数有一个层,更新的时候想删除,需要传递参数 Layers 不能为空,必须包含特殊元素:{ LayerName: '', LayerVersion: 0 }
Expand Down
4 changes: 2 additions & 2 deletions src/modules/triggers/apigw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ export default class ApigwTrigger extends BaseTrigger<ApigwTriggerInputsParams>
ep.function.functionName = inputs.functionName;
ep.function.functionNamespace = inputs.namespace || namespace || 'default';
ep.function.functionQualifier = ep.function.functionQualifier ?? '$DEFAULT';
// HTTP - Web 类型,EVENT - 时间类型
ep.function.functionType = ep.function.type === 'web' ? 'HTTP' : 'EVENT';
return ep;
}),
netTypes: parameters?.netTypes,
Expand All @@ -188,8 +190,6 @@ export default class ApigwTrigger extends BaseTrigger<ApigwTriggerInputsParams>
};
const triggerKey = this.getKey(triggerInputs);

console.log('triggerInputs', triggerInputs);

return {
triggerKey,
triggerInputs,
Expand Down