serverless-http-invoker 2.0.0-beta.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,9 @@ const expect = require("chai").expect
5
5
  const ServerlessInvoker = require("../../index")
6
6
 
7
7
  describe("basic", function () {
8
+ // in serverless v3 these started taking longer than the default 2000ms
9
+ this.timeout(2500)
10
+
8
11
  let sls = null
9
12
  beforeEach(function () {
10
13
  sls = new ServerlessInvoker(path.join(__dirname))
@@ -4,167 +4,173 @@ const path = require("path")
4
4
  const expect = require("chai").expect
5
5
  const ServerlessInvoker = require("../../index")
6
6
 
7
- let sls = null
8
-
9
- beforeEach(function () {
10
- process.chdir(path.join(__dirname))
11
- sls = new ServerlessInvoker(path.join(__dirname))
12
- })
13
-
14
- it("should work with callbacks too", function () {
15
- const response = sls.invoke("GET api/callback")
16
- return expect(response).to.eventually.have.property("statusCode", 200)
17
- })
7
+ describe("comprehensive", function () {
8
+ let sls = null
9
+ // in serverless v3 these started taking longer than the default 2000ms
10
+ this.timeout(2500)
11
+
12
+ beforeEach(function () {
13
+ process.chdir(path.join(__dirname))
14
+ sls = new ServerlessInvoker(path.join(__dirname))
15
+ })
18
16
 
19
- it("should invoke path with params", function () {
20
- const response = sls.invoke("GET api/hello/world")
21
- return expect(response).to.eventually.have.property("statusCode", 200)
22
- })
17
+ it("should work with callbacks too", function () {
18
+ const response = sls.invoke("GET api/callback")
19
+ return expect(response).to.eventually.have.property("statusCode", 200)
20
+ })
23
21
 
24
- it("should invoke path with shorthand", function () {
25
- const response = sls.invoke("GET api/shorthand")
26
- return expect(response).to.eventually.have.property("statusCode", 200)
27
- })
22
+ it("should invoke path with params", function () {
23
+ const response = sls.invoke("GET api/hello/world")
24
+ return expect(response).to.eventually.have.property("statusCode", 200)
25
+ })
28
26
 
29
- it("should invoke path with multiple params", function () {
30
- const response = sls.invoke("GET api/res1/1111/res2/2222")
31
- return expect(response).to.eventually.have.property("statusCode", 200)
32
- })
27
+ it("should invoke path with shorthand", function () {
28
+ const response = sls.invoke("GET /api/shorthand")
29
+ return expect(response).to.eventually.have.property("statusCode", 200)
30
+ })
33
31
 
34
- it("should parse json response body", function () {
35
- const response = sls.invoke("GET api/hello")
36
- expect(response).to.eventually.have.property("statusCode", 200)
37
- return expect(response).to.eventually.have.deep.nested.property(
38
- "body.message",
39
- "Go Serverless v1.0! Your function executed successfully!"
40
- )
41
- })
32
+ it("should invoke path with multiple params", function () {
33
+ const response = sls.invoke("GET /api/res1/1111/res2/2222")
34
+ return expect(response).to.eventually.have.property("statusCode", 200)
35
+ })
42
36
 
43
- it("should load environment", function () {
44
- const response = sls.invoke("GET api/env")
45
- expect(response).to.eventually.have.property("statusCode", 200)
46
- return expect(response).to.eventually.have.deep.nested.property(
47
- "body.message",
48
- "process.env.MY_SIMPLE==simple value"
49
- )
50
- })
37
+ it("should parse json response body", function () {
38
+ const response = sls.invoke("GET api/hello")
39
+ expect(response).to.eventually.have.property("statusCode", 200)
40
+ return expect(response).to.eventually.have.deep.nested.property(
41
+ "body.message",
42
+ "Go Serverless v1.0! Your function executed successfully!"
43
+ )
44
+ })
51
45
 
52
- it("should pass data to POST", function () {
53
- const response = sls.invoke("POST api/postit", { body: "boo" })
54
- return expect(response).to.eventually.have.deep.property("body", {
55
- message: "postit:boo",
46
+ it("should load environment", function () {
47
+ const response = sls.invoke("GET api/env")
48
+ expect(response).to.eventually.have.property("statusCode", 200)
49
+ return expect(response).to.eventually.have.deep.nested.property(
50
+ "body.message",
51
+ "process.env.MY_SIMPLE==simple value"
52
+ )
56
53
  })
57
- })
58
54
 
59
- it("should pass pathParameters with values when present", function () {
60
- const response = sls.invoke("GET api/res1/xxx/res2/yyy")
61
- return response.then((resp) => {
62
- return expect(resp.body.input).to.have.deep.property("pathParameters", {
63
- res1ID: "xxx",
64
- res2ID: "yyy",
55
+ it("should pass data to POST", function () {
56
+ const response = sls.invoke("POST api/postit", { body: "boo" })
57
+ return expect(response).to.eventually.have.deep.property("body", {
58
+ message: "postit:boo",
65
59
  })
66
60
  })
67
- })
68
61
 
69
- it("should pass pathParameters along with existing event too", function () {
70
- const response = sls.invoke("GET api/res1/xxx/res2/yyy", {
71
- requestPayload: "boo",
72
- })
73
- return response.then((resp) => {
74
- expect(resp.body).to.have.property("input")
75
- expect(resp.body.input).to.have.deep.property("pathParameters", {
76
- res1ID: "xxx",
77
- res2ID: "yyy",
62
+ it("should pass pathParameters with values when present", function () {
63
+ const response = sls.invoke("GET /api/res1/xxx/res2/yyy")
64
+ return response.then((resp) => {
65
+ return expect(resp.body.input).to.have.deep.property("pathParameters", {
66
+ res1ID: "xxx",
67
+ res2ID: "yyy",
68
+ })
78
69
  })
79
- expect(resp.body.input).to.have.property("requestPayload", "boo")
80
70
  })
81
- })
82
71
 
83
- it("should pass pathParameters empty when not present", function () {
84
- const response = sls.invoke("GET api/hello")
85
- return response.then((resp) => {
86
- return expect(resp.body.input).to.have.deep.property("pathParameters", {})
72
+ it("should pass pathParameters along with existing event too", function () {
73
+ const response = sls.invoke("GET /api/res1/xxx/res2/yyy", {
74
+ requestPayload: "boo",
75
+ })
76
+ return response.then((resp) => {
77
+ expect(resp.body).to.have.property("input")
78
+ expect(resp.body.input).to.have.deep.property("pathParameters", {
79
+ res1ID: "xxx",
80
+ res2ID: "yyy",
81
+ })
82
+ expect(resp.body.input).to.have.property("requestPayload", "boo")
83
+ })
87
84
  })
88
- })
89
85
 
90
- it("should pass greedy path params", function () {
91
- const response = sls.invoke("GET api/greedy/blah/blah/blah")
92
- return response.then((resp) => {
93
- return expect(resp.body.input).to.have.deep.property("pathParameters", {
94
- money: "blah/blah/blah",
86
+ it("should pass pathParameters empty when not present", function () {
87
+ const response = sls.invoke("GET api/hello")
88
+ return response.then((resp) => {
89
+ return expect(resp.body.input).to.have.deep.property("pathParameters", {})
95
90
  })
96
91
  })
97
- })
98
92
 
99
- it("should pass queryStringParameters with values when present", function () {
100
- const response = sls.invoke("GET api/with_querystring_params?p1=val1&p2=val2")
101
- return response.then((resp) => {
102
- return expect(resp.body.input).to.have.deep.property(
103
- "queryStringParameters",
104
- {
105
- p1: "val1",
106
- p2: "val2",
107
- }
108
- )
93
+ it("should pass greedy path params", function () {
94
+ const response = sls.invoke("GET api/greedy/blah/blah/blah")
95
+ return response.then((resp) => {
96
+ return expect(resp.body.input).to.have.deep.property("pathParameters", {
97
+ money: "blah/blah/blah",
98
+ })
99
+ })
109
100
  })
110
- })
111
101
 
112
- it("should pass queryStringParameters even when not present", function () {
113
- const response = sls.invoke("GET api/with_querystring_params")
114
- return response.then((resp) => {
115
- return expect(resp.body.input).to.have.deep.property(
116
- "queryStringParameters",
117
- {}
102
+ it("should pass queryStringParameters with values when present", function () {
103
+ const response = sls.invoke(
104
+ "GET api/with_querystring_params?p1=val1&p2=val2"
118
105
  )
106
+ return response.then((resp) => {
107
+ return expect(resp.body.input).to.have.deep.property(
108
+ "queryStringParameters",
109
+ {
110
+ p1: "val1",
111
+ p2: "val2",
112
+ }
113
+ )
114
+ })
119
115
  })
120
- })
121
116
 
122
- it("should match urls with query strings and path params in url", function () {
123
- const response = sls.invoke(
124
- "GET api/with_querystring_params_and_pathparams/ppvalue?qs1=qsval1"
125
- )
126
- return response.then((resp) => {
127
- expect(resp.body.input).to.have.deep.property("queryStringParameters", {
128
- qs1: "qsval1",
117
+ it("should pass queryStringParameters even when not present", function () {
118
+ const response = sls.invoke("GET api/with_querystring_params")
119
+ return response.then((resp) => {
120
+ return expect(resp.body.input).to.have.deep.property(
121
+ "queryStringParameters",
122
+ {}
123
+ )
129
124
  })
130
- return expect(resp.body.input).to.have.deep.property("pathParameters", {
131
- pathparam1: "ppvalue",
125
+ })
126
+
127
+ it("should match urls with query strings and path params in url", function () {
128
+ const response = sls.invoke(
129
+ "GET api/with_querystring_params_and_pathparams/ppvalue?qs1=qsval1"
130
+ )
131
+ return response.then((resp) => {
132
+ expect(resp.body.input).to.have.deep.property("queryStringParameters", {
133
+ qs1: "qsval1",
134
+ })
135
+ return expect(resp.body.input).to.have.deep.property("pathParameters", {
136
+ pathparam1: "ppvalue",
137
+ })
132
138
  })
133
139
  })
134
- })
135
140
 
136
- it("should marshal raw lambda exceptions back as http responses", function () {
137
- const response = sls.invoke("GET api/throwWorld")
138
- return expect(response).to.eventually.have.property("statusCode", 502)
139
- })
141
+ it("should marshal raw lambda exceptions back as http responses", function () {
142
+ const response = sls.invoke("GET api/throwWorld")
143
+ return expect(response).to.eventually.have.property("statusCode", 502)
144
+ })
140
145
 
141
- it("should marshal raw handled lambda errors back as http responses", function () {
142
- const response = sls.invoke("GET api/errorWorld")
143
- return expect(response).to.eventually.have.property("statusCode", 502)
144
- })
146
+ it("should marshal raw handled lambda errors back as http responses", function () {
147
+ const response = sls.invoke("GET api/errorWorld")
148
+ return expect(response).to.eventually.have.property("statusCode", 502)
149
+ })
145
150
 
146
- it("should error if path isn't found", function () {
147
- const response = sls.invoke("GET api/DOES_NOT_EXIST")
148
- return expect(response).to.eventually.be.rejectedWith(
149
- /^Serverless http event not found for HTTP request/
150
- )
151
- })
151
+ it("should error if path isn't found", function () {
152
+ const response = sls.invoke("GET api/DOES_NOT_EXIST")
153
+ return expect(response).to.eventually.be.rejectedWith(
154
+ /^Serverless http event not found for HTTP request/
155
+ )
156
+ })
152
157
 
153
- it("should try to find service path in same dir", function () {
154
- process.chdir(path.join(__dirname))
155
- const localSls = new ServerlessInvoker()
156
- expect(localSls.servicePath).to.match(/examples\/comprehensive$/)
157
- })
158
+ it("should try to find service path in same dir", function () {
159
+ process.chdir(path.join(__dirname))
160
+ const localSls = new ServerlessInvoker()
161
+ expect(localSls.servicePath).to.match(/examples\/comprehensive$/)
162
+ })
158
163
 
159
- it("should try to find service path in parent dir", function () {
160
- process.chdir(path.join(__dirname, "./subdir"))
161
- const localSls = new ServerlessInvoker()
162
- expect(localSls.servicePath).to.match(/examples\/comprehensive$/)
163
- })
164
+ it("should try to find service path in parent dir", function () {
165
+ process.chdir(path.join(__dirname, "./subdir"))
166
+ const localSls = new ServerlessInvoker()
167
+ expect(localSls.servicePath).to.match(/examples\/comprehensive$/)
168
+ })
164
169
 
165
- it("should fail if serverless.yml not found", function () {
166
- process.chdir(path.join(__dirname, "../../test-data/no-serverless-found"))
167
- expect(() => new ServerlessInvoker()).to.throw(
168
- /^Cannot find serverless.yml. Started search in working directory/
169
- )
170
+ it("should fail if serverless.yml not found", function () {
171
+ process.chdir(path.join(__dirname, "../../test-data/no-serverless-found"))
172
+ expect(() => new ServerlessInvoker()).to.throw(
173
+ /^Cannot find serverless.yml. Started search in working directory/
174
+ )
175
+ })
170
176
  })
@@ -34,7 +34,7 @@ functions:
34
34
  shorthand_path_syntax:
35
35
  handler: handler.hello
36
36
  events:
37
- - http: GET api/shorthand
37
+ - http: GET /api/shorthand
38
38
 
39
39
  malformed_http_event:
40
40
  handler: handler.hello
@@ -46,7 +46,7 @@ functions:
46
46
  path_with_multiple_params:
47
47
  handler: handler.hello
48
48
  events:
49
- - http: GET api/res1/{res1ID}/res2/{res2ID}
49
+ - http: GET /api/res1/{res1ID}/res2/{res2ID}
50
50
 
51
51
  env:
52
52
  handler: handler.env
package/index.js CHANGED
@@ -1,12 +1,13 @@
1
1
  "use strict"
2
2
  const path = require("path")
3
- const fs = require("fs")
3
+ const { existsSync } = require("fs")
4
+ const fsPromises = require("fs/promises")
4
5
  const Serverless = require("serverless")
5
- const Promise = require("bluebird")
6
6
  const { wrap } = require("lambda-wrapper")
7
7
  const assert = require("assert")
8
8
  const url = require("url")
9
9
  const querystring = require("querystring")
10
+ const YAML = require("yaml")
10
11
 
11
12
  class ServerlessInvoker {
12
13
  /**
@@ -14,14 +15,14 @@ class ServerlessInvoker {
14
15
  * @param {*string} servicePath Path to the directory containing the serverless file.
15
16
  */
16
17
  constructor(servicePath) {
17
- this.servicePath = servicePath || this.findServicePath()
18
+ this.servicePath = servicePath || ServerlessInvoker.findServicePath()
18
19
  this.serverless = null
19
20
  this.serverlessEvents = null
20
21
  }
21
22
 
22
- findServicePath() {
23
+ static findServicePath() {
23
24
  let dir = process.cwd()
24
- while (dir !== "/" && !fs.existsSync(path.join(dir, "serverless.yml"))) {
25
+ while (dir !== "/" && !existsSync(path.join(dir, "serverless.yml"))) {
25
26
  dir = path.dirname(dir)
26
27
  }
27
28
  if (dir === "/") {
@@ -32,14 +33,30 @@ class ServerlessInvoker {
32
33
  return dir
33
34
  }
34
35
 
35
- initializeServerless() {
36
+ static async loadServerlessYaml(path) {
37
+ const file = await fsPromises.readFile(path, "utf8")
38
+ return YAML.parse(file)
39
+ }
40
+
41
+ async initializeServerless() {
42
+ process.env.SLS_DEBUG = "*"
43
+ // Serverless v3 requires us to load the config manually: https://www.serverless.com/framework/docs/deprecations#serverless-constructor-service-configuration-dependency
44
+ // see also https://www.serverless.com/framework/docs/guides/upgrading-v3#low-level-changes
45
+ // https://www.serverless.com/framework/docs/deprecations#serverless-constructor-configcommands-and-configoptions-requirement
46
+ const slsService = await ServerlessInvoker.loadServerlessYaml(
47
+ path.join(this.servicePath, "serverless.yml")
48
+ )
36
49
  const config = {
37
- servicePath: this.servicePath,
50
+ serviceDir: path.dirname(this.servicePath),
51
+ configurationFilename: "serverless.yml",
52
+ configuration: slsService,
53
+ commands: [],
54
+ options: {},
38
55
  }
39
56
  const sls = new Serverless(config)
40
57
  // NOTE: I've seen sls.init() run very slowly; nearly 500ms!
41
58
  return sls.init().then(() => {
42
- return sls.variables.populateService().then(() => {
59
+ sls.service.load().then(() => {
43
60
  sls.service.setFunctionNames({})
44
61
  sls.service.mergeArrays()
45
62
  sls.service.validate()
@@ -54,7 +71,7 @@ class ServerlessInvoker {
54
71
  * @param {*object} event The event that should be submitted to the http endpoint.
55
72
  * @param {*object} context The context passed to the lambda function
56
73
  */
57
- invoke(httpRequest, event, context) {
74
+ async invoke(httpRequest, event, context) {
58
75
  // Read the serverless.yml file
59
76
  return this.initializeServerless()
60
77
  .then(() => this.loadServerlessEvents())
@@ -160,27 +177,22 @@ class ServerlessInvoker {
160
177
  return myURL.pathname
161
178
  }
162
179
 
163
- loadServerlessEnvironment() {
164
- return Promise.try(() => {
165
- let env = this.serverless.service.provider.environment
166
- Object.assign(process.env, env)
167
- return env
168
- })
180
+ async loadServerlessEnvironment() {
181
+ let env = this.serverless.service.provider.environment
182
+ Object.assign(process.env, env)
183
+ return env
169
184
  }
170
185
 
171
- invokeWithLambdaWrapper(httpEvent, event, context) {
172
- return Promise.try(() => {
173
- let handlerModule = require(path.join(
174
- this.servicePath,
175
- httpEvent.handlerPath
176
- ))
177
- let lambda = wrap(handlerModule, { handler: httpEvent.handlerName })
178
- lambda = Promise.promisifyAll(lambda)
179
- return lambda.runHandler(event, context || {})
180
- })
186
+ async invokeWithLambdaWrapper(httpEvent, event, context) {
187
+ const handlerModule = require(path.join(
188
+ this.servicePath,
189
+ httpEvent.handlerPath
190
+ ))
191
+ const lambda = wrap(handlerModule, { handler: httpEvent.handlerName })
192
+ return lambda.runHandler(event, context || {})
181
193
  }
182
194
 
183
- loadServerlessEvents() {
195
+ async loadServerlessEvents() {
184
196
  let funcs = this.serverless.service
185
197
  .getAllFunctions()
186
198
  .map((fname) => {
@@ -281,7 +293,7 @@ class ServerlessInvoker {
281
293
  events.push(e)
282
294
  }
283
295
  }
284
- return Promise.resolve(events)
296
+ return events
285
297
  }
286
298
  }
287
299
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serverless-http-invoker",
3
- "version": "2.0.0-beta.1",
3
+ "version": "3.0.0",
4
4
  "description": "Locally invoke Serverless functions via their HTTP event as specified in Serverless.yml for testing.",
5
5
  "main": "index.js",
6
6
  "author": "Scott Willeke <scott@willeke.com> (https:/scott.willeke.com/)",
@@ -40,10 +40,11 @@
40
40
  "lint-fix": "prettier --write \"{,!(node_modules)/**/}*.{ts,tsx,js,jsx,md,yml,json,html}\""
41
41
  },
42
42
  "dependencies": {
43
- "lambda-wrapper": "^0.3.0"
43
+ "lambda-wrapper": "^0.3.0",
44
+ "yaml": "^2.1.1"
44
45
  },
45
46
  "peerDependencies": {
46
- "serverless": "^1.27.3"
47
+ "serverless": "^3.22.0"
47
48
  },
48
49
  "devDependencies": {
49
50
  "chai": "^4.3.6",
@@ -52,6 +53,6 @@
52
53
  "mocha": "^10.0.0",
53
54
  "nyc": "^15.1.0",
54
55
  "prettier": "^2.7.1",
55
- "serverless": "^2.46.0"
56
+ "serverless": "^3.22.0"
56
57
  }
57
58
  }