commit
ec36588ae7
|
@ -23,9 +23,9 @@ services:
|
||||||
|
|
||||||
|
|
||||||
````
|
````
|
||||||
$ s exec -- invoke --invocation-type sync --event ${payload}
|
$ s invoke --invocation-type sync --event ${payload}
|
||||||
$ s exec -- invoke --invocation-type async --event-file ${path}
|
$ s invoke --invocation-type async --event-file ${path}
|
||||||
$ s exec -- invoke --event-stdin
|
$ s invoke --event-stdin
|
||||||
````
|
````
|
||||||
|
|
||||||
## CLI 用法
|
## CLI 用法
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
{
|
{
|
||||||
"body": 123,
|
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {
|
"headers": {
|
||||||
"key": "value"
|
"key": "value",
|
||||||
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
"queries": {
|
"queries": {
|
||||||
"key": "value"
|
"key": "value"
|
||||||
},
|
},
|
||||||
"path": "string"
|
"body": {
|
||||||
|
"abc": "body",
|
||||||
|
"tmpNasZipPath": "/tmpNasZipPath",
|
||||||
|
"body": "body"
|
||||||
|
},
|
||||||
|
"path": "download"
|
||||||
}
|
}
|
|
@ -28,7 +28,6 @@
|
||||||
"build": "ncc build src/index.ts -m -e @serverless-devs/core -o lib"
|
"build": "ncc build src/index.ts -m -e @serverless-devs/core -o lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alicloud/fc2": "^2.2.2",
|
|
||||||
"@serverless-devs/core": "^0.0.*",
|
"@serverless-devs/core": "^0.0.*",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
|
|
|
@ -75,9 +75,9 @@ export default [
|
||||||
content: [
|
content: [
|
||||||
'$ s invoke',
|
'$ s invoke',
|
||||||
'$ s <ProjectName> invoke',
|
'$ s <ProjectName> invoke',
|
||||||
'$ s exec -- invoke --invocation-type sync --event <payload>',
|
'$ s invoke --invocation-type sync --event <payload>',
|
||||||
'$ s exec -- invoke --event-file <file-path>',
|
'$ s invoke --event-file <file-path>',
|
||||||
'$ s exec -- invoke --event-stdin',
|
'$ s invoke --event-stdin',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,7 +5,6 @@ import HELP from './common/help';
|
||||||
import { InputProps, isProperties, IProperties } from './interface/entity';
|
import { InputProps, isProperties, IProperties } from './interface/entity';
|
||||||
// import StdoutFormatter from './common/stdout-formatter';
|
// import StdoutFormatter from './common/stdout-formatter';
|
||||||
import RemoteInvoke from './lib/remote-invoke';
|
import RemoteInvoke from './lib/remote-invoke';
|
||||||
import Client from './lib/client';
|
|
||||||
|
|
||||||
export default class FcRemoteInvoke {
|
export default class FcRemoteInvoke {
|
||||||
/**
|
/**
|
||||||
|
@ -30,7 +29,8 @@ export default class FcRemoteInvoke {
|
||||||
|
|
||||||
let fcClient;
|
let fcClient;
|
||||||
if (!props.domainName) {
|
if (!props.domainName) {
|
||||||
fcClient = await Client.buildFcClient(props.region, credentials);
|
const fcCommon = await core.loadComponent('devsapp/fc-common');
|
||||||
|
fcClient = await fcCommon.makeFcClient(inputs);
|
||||||
}
|
}
|
||||||
const remoteInvoke = new RemoteInvoke(fcClient, credentials.AccountID);
|
const remoteInvoke = new RemoteInvoke(fcClient, credentials.AccountID);
|
||||||
await remoteInvoke.invoke(props, eventPayload, { invocationType });
|
await remoteInvoke.invoke(props, eventPayload, { invocationType });
|
||||||
|
|
|
@ -1,114 +0,0 @@
|
||||||
import FC from '@alicloud/fc2';
|
|
||||||
import querystring from 'querystring';
|
|
||||||
import kitx from 'kitx';
|
|
||||||
import httpx from 'httpx';
|
|
||||||
import * as core from '@serverless-devs/core';
|
|
||||||
import { ICredentials } from '../interface/entity';
|
|
||||||
|
|
||||||
FC.prototype.costom_request = async function (method, path, query, body, headers = {}, opts = {}) {
|
|
||||||
var url = `${this.endpoint}/${this.version}${path}`;
|
|
||||||
if (query && Object.keys(query).length > 0) {
|
|
||||||
url = `${url}?${querystring.stringify(query)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
headers = Object.assign(this.buildHeaders(), this.headers, headers);
|
|
||||||
var postBody;
|
|
||||||
if (body) {
|
|
||||||
var buff = null;
|
|
||||||
if (Buffer.isBuffer(body)) {
|
|
||||||
buff = body;
|
|
||||||
headers['content-type'] = 'application/octet-stream';
|
|
||||||
} else if (typeof body === 'string') {
|
|
||||||
buff = new Buffer(body, 'utf8');
|
|
||||||
headers['content-type'] = 'application/octet-stream';
|
|
||||||
} else if ('function' === typeof body.pipe) {
|
|
||||||
buff = body;
|
|
||||||
headers['content-type'] = 'application/octet-stream';
|
|
||||||
} else {
|
|
||||||
buff = new Buffer(JSON.stringify(body), 'utf8');
|
|
||||||
headers['content-type'] = 'application/json';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('function' !== typeof body.pipe) {
|
|
||||||
const digest = kitx.md5(buff, 'hex');
|
|
||||||
const md5 = new Buffer(digest, 'utf8').toString('base64');
|
|
||||||
|
|
||||||
headers['content-length'] = buff.length;
|
|
||||||
headers['content-md5'] = md5;
|
|
||||||
}
|
|
||||||
postBody = buff;
|
|
||||||
}
|
|
||||||
|
|
||||||
var queriesToSign = null;
|
|
||||||
if (path.startsWith('/proxy/')) {
|
|
||||||
queriesToSign = query || {};
|
|
||||||
}
|
|
||||||
var signature = FC.getSignature(this.accessKeyID, this.accessKeySecret, method, `/${this.version}${path}`, headers, queriesToSign);
|
|
||||||
headers['authorization'] = signature;
|
|
||||||
|
|
||||||
const response = await httpx.request(url, {
|
|
||||||
method,
|
|
||||||
timeout: this.timeout,
|
|
||||||
headers,
|
|
||||||
data: postBody
|
|
||||||
});
|
|
||||||
|
|
||||||
var responseBody;
|
|
||||||
if (!opts['rawBuf'] || response.headers['x-fc-error-type']) {
|
|
||||||
responseBody = await httpx.read(response, 'utf8');
|
|
||||||
} else {
|
|
||||||
// @ts-ignore: .
|
|
||||||
responseBody = await httpx.read(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
const contentType = response.headers['content-type'] || '';
|
|
||||||
if (contentType.startsWith('application/json')) {
|
|
||||||
try {
|
|
||||||
responseBody = JSON.parse(responseBody);
|
|
||||||
} catch (ex) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let err;
|
|
||||||
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
||||||
const code = response.statusCode;
|
|
||||||
const requestid = response.headers['x-fc-request-id'];
|
|
||||||
var errMsg;
|
|
||||||
if (responseBody.ErrorMessage) {
|
|
||||||
errMsg = responseBody.ErrorMessage;
|
|
||||||
} else {
|
|
||||||
errMsg = responseBody.errorMessage;
|
|
||||||
}
|
|
||||||
err = new Error(`${method} ${path} failed with ${code}. requestid: ${requestid}, message: ${errMsg}.`);
|
|
||||||
err.name = `FC${responseBody.ErrorCode}Error`;
|
|
||||||
// @ts-ignore: .
|
|
||||||
err.code = responseBody.ErrorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
err,
|
|
||||||
code: response.statusCode,
|
|
||||||
'headers': response.headers,
|
|
||||||
'data': responseBody,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Client {
|
|
||||||
static async buildFcClient(region: string, credentials: ICredentials) {
|
|
||||||
return new FC(credentials.AccountID, {
|
|
||||||
accessKeyID: credentials.AccessKeyID,
|
|
||||||
accessKeySecret: credentials.AccessKeySecret,
|
|
||||||
securityToken: credentials.SecurityToken,
|
|
||||||
region,
|
|
||||||
endpoint: await this.getFcEndpoint(),
|
|
||||||
timeout: 6000000,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async getFcEndpoint(): Promise<string | undefined> {
|
|
||||||
const fcDefault = await core.loadComponent('devsapp/fc-default');
|
|
||||||
const fcEndpoint: string = await fcDefault.get({ args: 'fc-endpoint' });
|
|
||||||
if (!fcEndpoint) { return undefined; }
|
|
||||||
const enableFcEndpoint: any = await fcDefault.get({ args: 'enable-fc-endpoint' });
|
|
||||||
return (enableFcEndpoint === true || enableFcEndpoint === 'true') ? fcEndpoint : undefined;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,9 +16,10 @@ export default class File {
|
||||||
let input;
|
let input;
|
||||||
|
|
||||||
if (eventFile === '-') { // read from stdin
|
if (eventFile === '-') { // read from stdin
|
||||||
logger.log('Reading event data from stdin, which can be ended with Enter then Ctrl+D')
|
logger.log('Reading event data from stdin, which can be ended with Enter then Ctrl+D');
|
||||||
input = process.stdin;
|
input = process.stdin;
|
||||||
} else {
|
} else {
|
||||||
|
logger.log('Reading event file content:');
|
||||||
input = fs.createReadStream(eventFile, {
|
input = fs.createReadStream(eventFile, {
|
||||||
encoding: 'utf-8'
|
encoding: 'utf-8'
|
||||||
})
|
})
|
||||||
|
@ -32,7 +33,10 @@ export default class File {
|
||||||
rl.on('line', (line) => {
|
rl.on('line', (line) => {
|
||||||
event += line
|
event += line
|
||||||
})
|
})
|
||||||
rl.on('close', () => resolve(event))
|
rl.on('close', () => {
|
||||||
|
logger.log('');
|
||||||
|
resolve(event)
|
||||||
|
})
|
||||||
|
|
||||||
rl.on('SIGINT', () => reject(new Error('^C')))
|
rl.on('SIGINT', () => reject(new Error('^C')))
|
||||||
})
|
})
|
||||||
|
|
|
@ -101,7 +101,7 @@ export default class RemoteInvoke {
|
||||||
const q = qualifier ? `.${qualifier}` : '';
|
const q = qualifier ? `.${qualifier}` : '';
|
||||||
event.path = `/proxy/${serviceName}${q}/${functionName}/${event.path || ''}`;
|
event.path = `/proxy/${serviceName}${q}/${functionName}/${event.path || ''}`;
|
||||||
|
|
||||||
logger.log(`https://${this.accountId}.${region}.fc.aliyuncs.com/2016-08-15/proxy/${serviceName}${q}/${functionName}/`);
|
logger.log(`Request url: https://${this.accountId}.${region}.fc.aliyuncs.com/2016-08-15/proxy/${serviceName}${q}/${functionName}/`);
|
||||||
await this.request(event)
|
await this.request(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,10 @@ export default class RemoteInvoke {
|
||||||
* path 组装后的路径 /proxy/serviceName/functionName/path ,
|
* path 组装后的路径 /proxy/serviceName/functionName/path ,
|
||||||
*/
|
*/
|
||||||
async request(event) {
|
async request(event) {
|
||||||
const { headers, queries, method, path: p, body } = this.handlerHttpParmase(event);
|
const { headers = {}, queries, method = 'GET', path: p, body } = event;
|
||||||
|
if (!headers['X-Fc-Log-Type']) {
|
||||||
|
headers['X-Fc-Log-Type'] = 'Tail';
|
||||||
|
}
|
||||||
|
|
||||||
let resp;
|
let resp;
|
||||||
try {
|
try {
|
||||||
|
@ -125,8 +128,7 @@ export default class RemoteInvoke {
|
||||||
resp = await this.fcClient.costom_request('PUT', p, null, body, headers);
|
resp = await this.fcClient.costom_request('PUT', p, null, body, headers);
|
||||||
} else if (mt === 'DELETE') {
|
} else if (mt === 'DELETE') {
|
||||||
resp = await this.fcClient.costom_request('DELETE', p, queries, null, headers);
|
resp = await this.fcClient.costom_request('DELETE', p, queries, null, headers);
|
||||||
}
|
} else if (method.toLocaleUpperCase() === 'PATCH') {
|
||||||
else if (method.toLocaleUpperCase() === 'PATCH') {
|
|
||||||
resp = await this.fcClient.costom_request('PATCH', p, queries, body, headers);
|
resp = await this.fcClient.costom_request('PATCH', p, queries, body, headers);
|
||||||
} else if (method.toLocaleUpperCase() === 'HEAD') {
|
} else if (method.toLocaleUpperCase() === 'HEAD') {
|
||||||
resp = await this.fcClient.costom_request('HEAD', p, queries, body, headers);
|
resp = await this.fcClient.costom_request('HEAD', p, queries, body, headers);
|
||||||
|
@ -158,41 +160,6 @@ export default class RemoteInvoke {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlerHttpParmase (event) {
|
|
||||||
const { body = '', headers = {}, method = 'GET', queries = '', path: p = '' } = event;
|
|
||||||
|
|
||||||
let postBody;
|
|
||||||
if (body) {
|
|
||||||
let buff = null;
|
|
||||||
if (Buffer.isBuffer(body)) {
|
|
||||||
buff = body;
|
|
||||||
headers['content-type'] = 'application/octet-stream';
|
|
||||||
} else if (typeof body === 'string') {
|
|
||||||
buff = Buffer.from(body, 'utf8');
|
|
||||||
headers['content-type'] = 'application/octet-stream';
|
|
||||||
} else if (typeof body.pipe === 'function') {
|
|
||||||
buff = body;
|
|
||||||
headers['content-type'] = 'application/octet-stream';
|
|
||||||
} else {
|
|
||||||
buff = Buffer.from(JSON.stringify(body), 'utf8');
|
|
||||||
headers['content-type'] = 'application/json';
|
|
||||||
}
|
|
||||||
postBody = buff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!headers['X-Fc-Log-Type']) {
|
|
||||||
headers['X-Fc-Log-Type'] = 'Tail';
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
headers,
|
|
||||||
queries,
|
|
||||||
method,
|
|
||||||
path: p,
|
|
||||||
body: postBody
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private showLog(log) {
|
private showLog(log) {
|
||||||
if (log) {
|
if (log) {
|
||||||
logger.log('========= FC invoke Logs begin =========', 'yellow');
|
logger.log('========= FC invoke Logs begin =========', 'yellow');
|
||||||
|
|
Loading…
Reference in New Issue