1 | 'use strict';
|
2 |
|
3 | const BbPromise = require('bluebird');
|
4 | const chalk = require('chalk');
|
5 | const path = require('path');
|
6 |
|
7 | const CmdLineParamsOptions = {
|
8 | type: ['blocking', 'nonblocking'],
|
9 | log: ['result', 'response'],
|
10 | };
|
11 |
|
12 | class OpenWhiskInvoke {
|
13 | constructor(serverless, options) {
|
14 | this.serverless = serverless;
|
15 | this.options = options || {};
|
16 | this.provider = this.serverless.getProvider('openwhisk');
|
17 |
|
18 | this.hooks = {
|
19 | 'invoke:invoke': () => BbPromise.bind(this)
|
20 | .then(this.validate)
|
21 | .then(this.invoke)
|
22 | .then(this.log),
|
23 | };
|
24 | }
|
25 |
|
26 | validate() {
|
27 | if (!this.serverless.config.servicePath) {
|
28 | throw new this.serverless.classes.Error('This command can only be run inside a service.');
|
29 | }
|
30 |
|
31 | this.serverless.service.getFunction(this.options.function);
|
32 |
|
33 | if (this.options.path) {
|
34 | if (!this.serverless.utils
|
35 | .fileExistsSync(path.join(this.serverless.config.servicePath, this.options.path))) {
|
36 | throw new this.serverless.classes.Error('The file path you provided does not exist.');
|
37 | }
|
38 |
|
39 | this.options.data = this.serverless.utils
|
40 | .readFileSync(path.join(this.serverless.config.servicePath, this.options.path));
|
41 |
|
42 | if (this.options.data == null || typeof this.options.data !== 'object') {
|
43 | throw new this.serverless.classes.Error(
|
44 | 'The file path provided must point to a JSON file with a top-level JSON object definition.'
|
45 | );
|
46 | }
|
47 | } else if (this.options.data) {
|
48 | try {
|
49 | this.options.data = JSON.parse(this.options.data)
|
50 | } catch (e) {
|
51 | throw new this.serverless.classes.Error(
|
52 | 'Error parsing data parameter as JSON.'
|
53 | );
|
54 | }
|
55 | if (this.options.data == null || typeof this.options.data !== 'object') throw new this.serverless.classes.Error('Data parameter must be a JSON object')
|
56 | }
|
57 |
|
58 | this.validateParamOptions();
|
59 |
|
60 | return this.provider.client().then(client => {
|
61 | this.client = client;
|
62 | });
|
63 | }
|
64 |
|
65 |
|
66 | validateParamOptions() {
|
67 | Object.keys(CmdLineParamsOptions).forEach(key => {
|
68 | if (!this.options[key]) {
|
69 | this.options[key] = CmdLineParamsOptions[key][0];
|
70 | } else if (!CmdLineParamsOptions[key].find(i => i === this.options[key])) {
|
71 | const options = CmdLineParamsOptions[key].join(' or ');
|
72 | throw new this.serverless.classes.Error(
|
73 | `Invalid ${key} parameter value, must be either ${options}.`
|
74 | );
|
75 | }
|
76 | });
|
77 | }
|
78 |
|
79 | invoke() {
|
80 | const functionObject = this.serverless.service.getFunction(this.options.function);
|
81 |
|
82 | const options = {
|
83 | blocking: this.isBlocking(),
|
84 | actionName: functionObject.name
|
85 | || `${this.serverless.service.service}_${this.options.function}`,
|
86 | };
|
87 |
|
88 | if (functionObject.namespace) {
|
89 | options.namespace = functionObject.namespace;
|
90 | }
|
91 |
|
92 | if (this.options.data) {
|
93 | options.params = this.options.data;
|
94 | }
|
95 |
|
96 | return this.client.actions.invoke(options)
|
97 | .catch(err => this.formatErrMsg(err));
|
98 | }
|
99 |
|
100 | formatErrMsg (err) {
|
101 | let err_msg = `Failed to invoke function service (${this.options.function}) due to error:`
|
102 | const base_err = err.error
|
103 | if (base_err.response && base_err.response.result && typeof base_err.response.result.error === 'string') {
|
104 | err.message = base_err.response.result.error
|
105 | err_msg = `Failed to invoke function service (${this.options.function}) due to application error:`
|
106 | const logs_msg = ` Check logs for activation: ${base_err.activationId}`
|
107 | throw new this.serverless.classes.Error(`${err_msg}\n\n ${err.message}\n\n ${logs_msg}`)
|
108 | }
|
109 |
|
110 | throw new this.serverless.classes.Error(`${err_msg}\n\n ${err.message}`)
|
111 | }
|
112 |
|
113 | isBlocking() {
|
114 | return this.options.type === 'blocking';
|
115 | }
|
116 |
|
117 | isLogResult() {
|
118 | return this.options.log === 'result';
|
119 | }
|
120 |
|
121 | log(invocationReply) {
|
122 | let color = 'white';
|
123 |
|
124 |
|
125 | if (this.isBlocking() && !invocationReply.response.success) {
|
126 | color = 'red';
|
127 | }
|
128 |
|
129 | let result = invocationReply;
|
130 |
|
131 |
|
132 | if (this.isBlocking() && this.isLogResult()) {
|
133 | result = invocationReply.response.result;
|
134 | }
|
135 |
|
136 | this.consoleLog(chalk[color](JSON.stringify(result, null, 4)));
|
137 | return BbPromise.resolve();
|
138 | }
|
139 |
|
140 | consoleLog(msg) {
|
141 | console.log(msg);
|
142 | }
|
143 | }
|
144 |
|
145 | module.exports = OpenWhiskInvoke;
|