@yopdev/dev-server 3.0.2 → 3.0.3-alpha.2
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.
- package/.github/workflows/npm-publish.yml +3 -1
- package/__tests__/cloudformation-lambda-http-proxy.test.ts +89 -0
- package/__tests__/lambda-function-with-none-auth.template +13 -0
- package/__tests__/lambda-http-proxy.test.ts +3 -2
- package/__tests__/simple-lambda-function.template +11 -0
- package/__tests__/stub-handlers.ts +9 -0
- package/__tests__/test-model.ts +11 -0
- package/dist/__tests__/cloudformation-lambda-http-proxy.test.d.ts +1 -0
- package/dist/__tests__/cloudformation-lambda-http-proxy.test.js +58 -0
- package/dist/__tests__/lambda-http-proxy.test.js +2 -1
- package/dist/__tests__/stub-handlers.d.ts +5 -0
- package/dist/__tests__/stub-handlers.js +14 -0
- package/dist/__tests__/test-model.d.ts +11 -0
- package/dist/__tests__/test-model.js +15 -0
- package/dist/src/cloudformation-lambda-http-proxy.d.ts +3 -2
- package/dist/src/cloudformation-lambda-http-proxy.js +3 -1
- package/dist/src/cloudformation.js +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -2
- package/dist/src/lambda-http-proxy.d.ts +4 -4
- package/dist/src/lambda-http-proxy.js +7 -4
- package/dist/src/mappers.d.ts +3 -1
- package/jest.config.js +1 -1
- package/package.json +1 -1
- package/src/cloudformation-lambda-http-proxy.ts +11 -5
- package/src/cloudformation.ts +1 -1
- package/src/index.ts +1 -1
- package/src/lambda-http-proxy.ts +11 -5
- package/src/mappers.ts +2 -2
- package/tsconfig.json +1 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { newLambdaProxyFromCloudFormationTemplate } from "../src/cloudformation-lambda-http-proxy";
|
|
3
|
+
import { v2 } from "../src/mappers";
|
|
4
|
+
import { DevServer, promised } from "../src";
|
|
5
|
+
import axios from "axios";
|
|
6
|
+
import { STUB_HANDLER_CALLED_BODY } from "./stub-handlers";
|
|
7
|
+
import { alwaysDeny } from "./test-model";
|
|
8
|
+
|
|
9
|
+
describe("the cloudformation lambda http proxy", () => {
|
|
10
|
+
it("parses a lambda function", async () => {
|
|
11
|
+
let endpoint: string;
|
|
12
|
+
const devServer = await new DevServer('cfn-simple-lambda',
|
|
13
|
+
promised(
|
|
14
|
+
(c) => newLambdaProxyFromCloudFormationTemplate(
|
|
15
|
+
'cfn-simple-lambda',
|
|
16
|
+
{ protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
17
|
+
v2(),
|
|
18
|
+
{
|
|
19
|
+
extraRoutes: [],
|
|
20
|
+
template: () => './__tests__/simple-lambda-function.template',
|
|
21
|
+
handlers: () => '../dist/__tests__/stub-handlers.js',
|
|
22
|
+
handlerNameResolver: (n) => n,
|
|
23
|
+
},
|
|
24
|
+
async (url) => { endpoint = url; }
|
|
25
|
+
).start(c))
|
|
26
|
+
).start()
|
|
27
|
+
|
|
28
|
+
return expect(axios.get(`${endpoint}a`)
|
|
29
|
+
.then((r) => r.data)
|
|
30
|
+
.finally(async () => devServer.stop())
|
|
31
|
+
)
|
|
32
|
+
.resolves
|
|
33
|
+
.toEqual(STUB_HANDLER_CALLED_BODY)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it("parses a lambda function with authorizer", async () => {
|
|
37
|
+
let endpoint: string;
|
|
38
|
+
const devServer = await new DevServer('cfn-authorized-lambda',
|
|
39
|
+
promised(
|
|
40
|
+
(c) => newLambdaProxyFromCloudFormationTemplate(
|
|
41
|
+
'cfn-authorized-lambda',
|
|
42
|
+
{ protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
43
|
+
v2(),
|
|
44
|
+
{
|
|
45
|
+
extraRoutes: [],
|
|
46
|
+
authorizer: () => async () => alwaysDeny(),
|
|
47
|
+
template: () => './__tests__/simple-lambda-function.template',
|
|
48
|
+
handlers: () => '../dist/__tests__/stub-handlers.js',
|
|
49
|
+
handlerNameResolver: (n) => n,
|
|
50
|
+
},
|
|
51
|
+
async (url) => { endpoint = url; }
|
|
52
|
+
).start(c))
|
|
53
|
+
).start()
|
|
54
|
+
|
|
55
|
+
return expect(axios.get(`${endpoint}a`, { validateStatus: () => true })
|
|
56
|
+
.then((r) => r.status)
|
|
57
|
+
.finally(async () => devServer.stop())
|
|
58
|
+
)
|
|
59
|
+
.resolves
|
|
60
|
+
.toEqual(401)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it("disables the authorizer when auth is set to NONE", async () => {
|
|
64
|
+
let endpoint: string;
|
|
65
|
+
const devServer = await new DevServer('cfn-none-auth-lambda',
|
|
66
|
+
promised(
|
|
67
|
+
(c) => newLambdaProxyFromCloudFormationTemplate(
|
|
68
|
+
'cfn-none-auth-lambda',
|
|
69
|
+
{ protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
70
|
+
v2(),
|
|
71
|
+
{
|
|
72
|
+
extraRoutes: [],
|
|
73
|
+
authorizer: () => async () => alwaysDeny(),
|
|
74
|
+
template: () => './__tests__/lambda-function-with-none-auth.template',
|
|
75
|
+
handlers: () => '../dist/__tests__/stub-handlers.js',
|
|
76
|
+
handlerNameResolver: (n) => n,
|
|
77
|
+
},
|
|
78
|
+
async (url) => { endpoint = url; }
|
|
79
|
+
).start(c))
|
|
80
|
+
).start()
|
|
81
|
+
|
|
82
|
+
return expect(axios.get(`${endpoint}a`)
|
|
83
|
+
.then((r) => r.data)
|
|
84
|
+
.finally(async () => devServer.stop())
|
|
85
|
+
)
|
|
86
|
+
.resolves
|
|
87
|
+
.toEqual(STUB_HANDLER_CALLED_BODY)
|
|
88
|
+
})
|
|
89
|
+
})
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { describe, it, expect } from "@jest/globals";
|
|
2
2
|
import { DevServer } from "../src";
|
|
3
|
-
import {
|
|
3
|
+
import { newLambdaHttpProxy } from "../src/lambda-http-proxy";
|
|
4
4
|
import axios from 'axios';
|
|
5
5
|
import { v2 } from "../src/mappers";
|
|
6
|
+
import { alwaysDeny } from "./test-model";
|
|
6
7
|
|
|
7
8
|
describe("the lambda http proxy", () => {
|
|
8
9
|
const payloadVersionV2Mapper = v2().mapper;
|
|
@@ -58,7 +59,7 @@ describe("the lambda http proxy", () => {
|
|
|
58
59
|
{
|
|
59
60
|
settings: { protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
60
61
|
routes: [
|
|
61
|
-
{ method: /.*/, path: /.*/, weight: 0, handler: () => Promise.resolve({ statusCode: 200, body: 'handled!' }), authorizer: () =>
|
|
62
|
+
{ method: /.*/, path: /.*/, weight: 0, handler: () => Promise.resolve({ statusCode: 200, body: 'handled!' }), authorizer: async () => alwaysDeny() },
|
|
62
63
|
],
|
|
63
64
|
mapper: payloadVersionV2Mapper,
|
|
64
65
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const globals_1 = require("@jest/globals");
|
|
7
|
+
const cloudformation_lambda_http_proxy_1 = require("../src/cloudformation-lambda-http-proxy");
|
|
8
|
+
const mappers_1 = require("../src/mappers");
|
|
9
|
+
const src_1 = require("../src");
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
11
|
+
const stub_handlers_1 = require("./stub-handlers");
|
|
12
|
+
const test_model_1 = require("./test-model");
|
|
13
|
+
(0, globals_1.describe)("the cloudformation lambda http proxy", () => {
|
|
14
|
+
(0, globals_1.it)("parses a lambda function", async () => {
|
|
15
|
+
let endpoint;
|
|
16
|
+
const devServer = await new src_1.DevServer('cfn-simple-lambda', (0, src_1.promised)((c) => (0, cloudformation_lambda_http_proxy_1.newLambdaProxyFromCloudFormationTemplate)('cfn-simple-lambda', { protocol: 'http:', host: '127.0.0.1', port: undefined }, (0, mappers_1.v2)(), {
|
|
17
|
+
extraRoutes: [],
|
|
18
|
+
template: () => './__tests__/simple-lambda-function.template',
|
|
19
|
+
handlers: () => '../dist/__tests__/stub-handlers.js',
|
|
20
|
+
handlerNameResolver: (n) => n,
|
|
21
|
+
}, async (url) => { endpoint = url; }).start(c))).start();
|
|
22
|
+
return (0, globals_1.expect)(axios_1.default.get(`${endpoint}a`)
|
|
23
|
+
.then((r) => r.data)
|
|
24
|
+
.finally(async () => devServer.stop()))
|
|
25
|
+
.resolves
|
|
26
|
+
.toEqual(stub_handlers_1.STUB_HANDLER_CALLED_BODY);
|
|
27
|
+
});
|
|
28
|
+
(0, globals_1.it)("parses a lambda function with authorizer", async () => {
|
|
29
|
+
let endpoint;
|
|
30
|
+
const devServer = await new src_1.DevServer('cfn-authorized-lambda', (0, src_1.promised)((c) => (0, cloudformation_lambda_http_proxy_1.newLambdaProxyFromCloudFormationTemplate)('cfn-authorized-lambda', { protocol: 'http:', host: '127.0.0.1', port: undefined }, (0, mappers_1.v2)(), {
|
|
31
|
+
extraRoutes: [],
|
|
32
|
+
authorizer: () => async () => (0, test_model_1.alwaysDeny)(),
|
|
33
|
+
template: () => './__tests__/simple-lambda-function.template',
|
|
34
|
+
handlers: () => '../dist/__tests__/stub-handlers.js',
|
|
35
|
+
handlerNameResolver: (n) => n,
|
|
36
|
+
}, async (url) => { endpoint = url; }).start(c))).start();
|
|
37
|
+
return (0, globals_1.expect)(axios_1.default.get(`${endpoint}a`, { validateStatus: () => true })
|
|
38
|
+
.then((r) => r.status)
|
|
39
|
+
.finally(async () => devServer.stop()))
|
|
40
|
+
.resolves
|
|
41
|
+
.toEqual(401);
|
|
42
|
+
});
|
|
43
|
+
(0, globals_1.it)("disables the authorizer when auth is set to NONE", async () => {
|
|
44
|
+
let endpoint;
|
|
45
|
+
const devServer = await new src_1.DevServer('cfn-none-auth-lambda', (0, src_1.promised)((c) => (0, cloudformation_lambda_http_proxy_1.newLambdaProxyFromCloudFormationTemplate)('cfn-none-auth-lambda', { protocol: 'http:', host: '127.0.0.1', port: undefined }, (0, mappers_1.v2)(), {
|
|
46
|
+
extraRoutes: [],
|
|
47
|
+
authorizer: () => async () => (0, test_model_1.alwaysDeny)(),
|
|
48
|
+
template: () => './__tests__/lambda-function-with-none-auth.template',
|
|
49
|
+
handlers: () => '../dist/__tests__/stub-handlers.js',
|
|
50
|
+
handlerNameResolver: (n) => n,
|
|
51
|
+
}, async (url) => { endpoint = url; }).start(c))).start();
|
|
52
|
+
return (0, globals_1.expect)(axios_1.default.get(`${endpoint}a`)
|
|
53
|
+
.then((r) => r.data)
|
|
54
|
+
.finally(async () => devServer.stop()))
|
|
55
|
+
.resolves
|
|
56
|
+
.toEqual(stub_handlers_1.STUB_HANDLER_CALLED_BODY);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -8,6 +8,7 @@ const src_1 = require("../src");
|
|
|
8
8
|
const lambda_http_proxy_1 = require("../src/lambda-http-proxy");
|
|
9
9
|
const axios_1 = __importDefault(require("axios"));
|
|
10
10
|
const mappers_1 = require("../src/mappers");
|
|
11
|
+
const test_model_1 = require("./test-model");
|
|
11
12
|
(0, globals_1.describe)("the lambda http proxy", () => {
|
|
12
13
|
const payloadVersionV2Mapper = (0, mappers_1.v2)().mapper;
|
|
13
14
|
(0, globals_1.it)("invokes the handler with the heaviest route", async () => {
|
|
@@ -47,7 +48,7 @@ const mappers_1 = require("../src/mappers");
|
|
|
47
48
|
const tested = await new src_1.DevServer('lambda-http-proxy', (0, lambda_http_proxy_1.newLambdaHttpProxy)('service', {
|
|
48
49
|
settings: { protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
49
50
|
routes: [
|
|
50
|
-
{ method: /.*/, path: /.*/, weight: 0, handler: () => Promise.resolve({ statusCode: 200, body: 'handled!' }), authorizer: () =>
|
|
51
|
+
{ method: /.*/, path: /.*/, weight: 0, handler: () => Promise.resolve({ statusCode: 200, body: 'handled!' }), authorizer: async () => (0, test_model_1.alwaysDeny)() },
|
|
51
52
|
],
|
|
52
53
|
mapper: payloadVersionV2Mapper,
|
|
53
54
|
}, async (url) => { endpoint = url; })).start();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stubHandler = exports.STUB_HANDLER_CALLED_BODY = void 0;
|
|
4
|
+
exports.STUB_HANDLER_CALLED_BODY = 'stub handler called';
|
|
5
|
+
const stubHandler = () => new StubHandler().handle();
|
|
6
|
+
exports.stubHandler = stubHandler;
|
|
7
|
+
class StubHandler {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.handle = () => ({
|
|
10
|
+
statusCode: 200,
|
|
11
|
+
body: exports.STUB_HANDLER_CALLED_BODY,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.alwaysDeny = void 0;
|
|
4
|
+
const alwaysDeny = () => ({
|
|
5
|
+
principalId: '',
|
|
6
|
+
policyDocument: {
|
|
7
|
+
Version: '',
|
|
8
|
+
Statement: [{
|
|
9
|
+
Effect: 'Deny',
|
|
10
|
+
NotAction: [],
|
|
11
|
+
NotPrincipal: [],
|
|
12
|
+
}]
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
exports.alwaysDeny = alwaysDeny;
|
|
@@ -4,8 +4,9 @@ import { HttpSettings } from './http-server';
|
|
|
4
4
|
import { Authorizer, Route } from './lambda-http-proxy';
|
|
5
5
|
import { Callback, Service, Startable } from './services';
|
|
6
6
|
import { LambdaPayloadVersion } from './mappers';
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { APIGatewayAuthorizerResult } from 'aws-lambda';
|
|
8
|
+
export declare const newLambdaProxyFromCloudFormationTemplate: <Context, AuthorizerContext extends APIGatewayAuthorizerResult, Event_1, HandlerResponse>(name: string, settings: HttpSettings, payloadVersion: LambdaPayloadVersion<AuthorizerContext, Event_1, HandlerResponse>, config: CloudFormationLambdaProxyConfig<Context, AuthorizerContext, Event_1, HandlerResponse>, callback: Callback<string>) => Startable<Service<string>>;
|
|
9
|
+
type CloudFormationLambdaProxyConfig<Context, AuthorizerContext extends APIGatewayAuthorizerResult, Event, HandlerResponse> = {
|
|
9
10
|
authorizer?: (config: DevServerConfig) => Authorizer<AuthorizerContext> | undefined;
|
|
10
11
|
extraRoutes: Route<Context, AuthorizerContext, Event, HandlerResponse>[];
|
|
11
12
|
prepare?: (config: DevServerConfig) => Promise<void>;
|
|
@@ -6,6 +6,7 @@ const lambda_http_proxy_1 = require("./lambda-http-proxy");
|
|
|
6
6
|
const PATH_VARIABLE_CAPTURE = /{(.*?)}/g;
|
|
7
7
|
const QUERY_STRING_OR_LOCATION_REG_EXP = '(?:([?#].*))?';
|
|
8
8
|
const PROXY_PATH_PARAM = 'proxy';
|
|
9
|
+
const NOOP_AUTHORIZER = async () => undefined;
|
|
9
10
|
const newLambdaProxyFromCloudFormationTemplate = (name, settings, payloadVersion, config, callback) => new CloudFormationLambdaProxy(name, settings, config.extraRoutes, payloadVersion, config, callback);
|
|
10
11
|
exports.newLambdaProxyFromCloudFormationTemplate = newLambdaProxyFromCloudFormationTemplate;
|
|
11
12
|
class CloudFormationLambdaProxy extends cloudformation_1.CloudFormationSetup {
|
|
@@ -15,6 +16,7 @@ class CloudFormationLambdaProxy extends cloudformation_1.CloudFormationSetup {
|
|
|
15
16
|
path: new RegExp(`^${pathWithCapturingGroups}${QUERY_STRING_OR_LOCATION_REG_EXP}?$`),
|
|
16
17
|
weight: this.computeWeight(pathWithCapturingGroups),
|
|
17
18
|
handler: this.pathParameterCapture(new RegExp(pathWithCapturingGroups), handler, config.context),
|
|
19
|
+
authorizer: event.Properties?.Auth?.Authorizer === 'NONE' ? NOOP_AUTHORIZER : undefined,
|
|
18
20
|
})), config.prepare, config);
|
|
19
21
|
this.settings = settings;
|
|
20
22
|
this.extraRoutes = extraRoutes;
|
|
@@ -22,7 +24,7 @@ class CloudFormationLambdaProxy extends cloudformation_1.CloudFormationSetup {
|
|
|
22
24
|
this.afterStart = (name, cfg, routes) => (0, lambda_http_proxy_1.newLambdaHttpProxy)(`${name}:apigw`, {
|
|
23
25
|
settings: this.settings,
|
|
24
26
|
routes: this.extraRoutes.concat(routes),
|
|
25
|
-
authorizer: (this.authorizer || (() =>
|
|
27
|
+
authorizer: (this.authorizer || (() => NOOP_AUTHORIZER))(cfg),
|
|
26
28
|
mapper: this.mapper,
|
|
27
29
|
}, this.callback);
|
|
28
30
|
this.computeWeight = (path) => this.segments(path) - this.variables(path);
|
|
@@ -12,7 +12,7 @@ class CloudFormationSetup {
|
|
|
12
12
|
this.eventFilter = eventFilter;
|
|
13
13
|
this.handlerResolver = handlerResolver;
|
|
14
14
|
this.beforeStart = beforeStart;
|
|
15
|
-
this.start = async (config) => (this.beforeStart ||
|
|
15
|
+
this.start = async (config) => (this.beforeStart || (async () => undefined))(config)
|
|
16
16
|
.then(this.resolvedHandlers)
|
|
17
17
|
.then(async (routes) => this.afterStart(this.name, config, routes));
|
|
18
18
|
this.resolvedHandlers = async () => {
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { DevServer } from './dev-server';
|
|
2
2
|
export { newHttpServer as httpServer, HttpSettings } from './http-server';
|
|
3
|
-
export { newLambdaHttpProxy as lambdaHttpProxy
|
|
3
|
+
export { newLambdaHttpProxy as lambdaHttpProxy } from './lambda-http-proxy';
|
|
4
4
|
export { v1 as v1LambdaProxyPayload, v2 as v2LambdaProxyPayload } from './mappers';
|
|
5
5
|
export { newLambdaProxyFromCloudFormationTemplate as cloudFormationLambdaProxy } from './cloudformation-lambda-http-proxy';
|
|
6
6
|
export { newDynamoDbTableFromCloudFormationTemplate as cloudFormationDynamoDbTable } from './cloudformation-dynamodb-table';
|
package/dist/src/index.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Service = exports.promised = exports.lazy = exports.oneThenOther = exports.allOf = exports.tunnel = exports.container = exports.dynamoDbTable = exports.preTrafficHooks = exports.scheduledTasks = exports.cloudFormationSnsEventHandlers = exports.eventsProxy = exports.internalQueue = exports.snsHttpProxy = exports.cloudFormationDynamoDbTable = exports.cloudFormationLambdaProxy = exports.v2LambdaProxyPayload = exports.v1LambdaProxyPayload = exports.
|
|
3
|
+
exports.Service = exports.promised = exports.lazy = exports.oneThenOther = exports.allOf = exports.tunnel = exports.container = exports.dynamoDbTable = exports.preTrafficHooks = exports.scheduledTasks = exports.cloudFormationSnsEventHandlers = exports.eventsProxy = exports.internalQueue = exports.snsHttpProxy = exports.cloudFormationDynamoDbTable = exports.cloudFormationLambdaProxy = exports.v2LambdaProxyPayload = exports.v1LambdaProxyPayload = exports.lambdaHttpProxy = exports.httpServer = exports.DevServer = void 0;
|
|
4
4
|
var dev_server_1 = require("./dev-server");
|
|
5
5
|
Object.defineProperty(exports, "DevServer", { enumerable: true, get: function () { return dev_server_1.DevServer; } });
|
|
6
6
|
var http_server_1 = require("./http-server");
|
|
7
7
|
Object.defineProperty(exports, "httpServer", { enumerable: true, get: function () { return http_server_1.newHttpServer; } });
|
|
8
8
|
var lambda_http_proxy_1 = require("./lambda-http-proxy");
|
|
9
9
|
Object.defineProperty(exports, "lambdaHttpProxy", { enumerable: true, get: function () { return lambda_http_proxy_1.newLambdaHttpProxy; } });
|
|
10
|
-
Object.defineProperty(exports, "UNAUTHORIZED", { enumerable: true, get: function () { return lambda_http_proxy_1.UNAUTHORIZED; } });
|
|
11
10
|
var mappers_1 = require("./mappers");
|
|
12
11
|
Object.defineProperty(exports, "v1LambdaProxyPayload", { enumerable: true, get: function () { return mappers_1.v1; } });
|
|
13
12
|
Object.defineProperty(exports, "v2LambdaProxyPayload", { enumerable: true, get: function () { return mappers_1.v2; } });
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { HttpSettings } from "./http-server";
|
|
3
3
|
import { LambdaMapperFactory } from "./mappers";
|
|
4
4
|
import { Service, Callback } from "./services";
|
|
5
|
-
|
|
6
|
-
export declare const newLambdaHttpProxy: <Context, AuthorizerContext, Event_1, HandlerResponse>(name: string, config: {
|
|
5
|
+
import { APIGatewayAuthorizerResult } from "aws-lambda";
|
|
6
|
+
export declare const newLambdaHttpProxy: <Context, AuthorizerContext extends APIGatewayAuthorizerResult, Event_1, HandlerResponse>(name: string, config: {
|
|
7
7
|
settings: HttpSettings;
|
|
8
8
|
routes: Route<Context, AuthorizerContext, Event_1, HandlerResponse>[];
|
|
9
9
|
mapper: LambdaMapperFactory<AuthorizerContext, Event_1, HandlerResponse>;
|
|
@@ -18,11 +18,11 @@ export declare abstract class Response {
|
|
|
18
18
|
constructor(statusCode: number, contentType: string | undefined, location: string | undefined, cookies: string[] | undefined);
|
|
19
19
|
body: () => string | Buffer | undefined;
|
|
20
20
|
}
|
|
21
|
-
export type Route<Context, AuthorizerContext, Event, HandlerResponse> = {
|
|
21
|
+
export type Route<Context, AuthorizerContext extends APIGatewayAuthorizerResult, Event, HandlerResponse> = {
|
|
22
22
|
method: RegExp;
|
|
23
23
|
path: RegExp;
|
|
24
24
|
weight: number;
|
|
25
25
|
authorizer?: Authorizer<AuthorizerContext>;
|
|
26
26
|
handler: (event: Event, context: Context) => Promise<HandlerResponse>;
|
|
27
27
|
};
|
|
28
|
-
export type Authorizer<Context> = (authorization: string) => Promise<Context | undefined>;
|
|
28
|
+
export type Authorizer<Context extends APIGatewayAuthorizerResult> = (authorization: string) => Promise<Context | undefined>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Response = exports.newLambdaHttpProxy =
|
|
3
|
+
exports.Response = exports.newLambdaHttpProxy = void 0;
|
|
4
4
|
const logging_1 = require("@yopdev/logging");
|
|
5
5
|
const http_server_1 = require("./http-server");
|
|
6
6
|
const responses_1 = require("./responses");
|
|
7
7
|
const services_1 = require("./services");
|
|
8
|
-
|
|
8
|
+
const UNAUTHORIZED = new Error('UNAUTHORIZED');
|
|
9
9
|
const newLambdaHttpProxy = (name, config, callback) => new services_1.Service(new LambdaHttpProxy(name, config.settings, config.routes, config.mapper, config.authorizer ?? (() => Promise.resolve(undefined)), config.context), callback);
|
|
10
10
|
exports.newLambdaHttpProxy = newLambdaHttpProxy;
|
|
11
11
|
class LambdaHttpProxy {
|
|
@@ -19,7 +19,10 @@ class LambdaHttpProxy {
|
|
|
19
19
|
this.stop = () => this.server.stop();
|
|
20
20
|
this.handler = (authorizer, lambdaHandler) => (request, body, response) => this.mapper.newInstance(request, body)
|
|
21
21
|
.then(async (mapper) => authorizer(mapper.authorization())
|
|
22
|
-
.then(async (context) =>
|
|
22
|
+
.then(async (context) => context?.policyDocument?.Statement?.find((s) => s.Effect === 'Deny') === undefined
|
|
23
|
+
? { ...context, lambda: context?.context }
|
|
24
|
+
: Promise.reject(UNAUTHORIZED))
|
|
25
|
+
.then((context) => lambdaHandler(mapper.event(context), this.context())
|
|
23
26
|
.then((lambda) => {
|
|
24
27
|
const result = mapper.toResponse(lambda);
|
|
25
28
|
return (0, responses_1.writeResponse)(response, result.statusCode, result.body(), result.contentType, result.location, result.cookies);
|
|
@@ -27,7 +30,7 @@ class LambdaHttpProxy {
|
|
|
27
30
|
.catch((e) => {
|
|
28
31
|
this.LOGGER.error(e, 'request failed to execute');
|
|
29
32
|
(0, responses_1.internalServerError)(response, e.body);
|
|
30
|
-
}), (e) => e ===
|
|
33
|
+
}), (e) => e === UNAUTHORIZED ? (0, responses_1.writeResponse)(response, 401, '') : (0, responses_1.internalServerError)(response, e)));
|
|
31
34
|
this.fallback = async (request, _, response) => Promise.resolve(this.LOGGER.warn(`FALLBACK: ${request.method} to ${request.url}`)).then(() => (0, responses_1.writeResponse)(response, 404, `no route found to handle ${request.method} to ${request.url}`));
|
|
32
35
|
this.resolveRoute = (request, body, response) => (this.routes.filter((r) => r.method.test(request.method))
|
|
33
36
|
.filter((r) => r.path.test(request.url))
|
package/dist/src/mappers.d.ts
CHANGED
|
@@ -11,7 +11,9 @@ export type LambdaPayloadVersion<AuthorizerContext, Event, HandlerResponse> = {
|
|
|
11
11
|
export declare const v1: <AuthorizerContext>() => LambdaPayloadVersion<AuthorizerContext, APIGatewayProxyEvent, APIGatewayProxyResult>;
|
|
12
12
|
export declare const v2: <AuthorizerContext>() => LambdaPayloadVersion<AuthorizerContext, APIGatewayProxyEventV2, APIGatewayProxyStructuredResultV2>;
|
|
13
13
|
interface LambdaMapper<AuthorizerContext, Event, HandlerResponse> {
|
|
14
|
-
event(context:
|
|
14
|
+
event(context: {
|
|
15
|
+
[name: string]: any;
|
|
16
|
+
} & AuthorizerContext): Event;
|
|
15
17
|
authorization(): string;
|
|
16
18
|
toResponse: (response: HandlerResponse) => Response;
|
|
17
19
|
}
|
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -4,11 +4,13 @@ import { HttpSettings } from './http-server';
|
|
|
4
4
|
import { Authorizer, Route, newLambdaHttpProxy } from './lambda-http-proxy';
|
|
5
5
|
import { Callback, Service, Startable } from './services';
|
|
6
6
|
import { LambdaMapperFactory, LambdaPayloadVersion, PathParameterResolver } from './mappers';
|
|
7
|
+
import { APIGatewayAuthorizerResult } from 'aws-lambda';
|
|
7
8
|
|
|
8
9
|
const PATH_VARIABLE_CAPTURE = /{(.*?)}/g;
|
|
9
10
|
const QUERY_STRING_OR_LOCATION_REG_EXP = '(?:([?#].*))?';
|
|
10
11
|
const PROXY_PATH_PARAM = 'proxy';
|
|
11
|
-
|
|
12
|
+
const NOOP_AUTHORIZER = async () => undefined
|
|
13
|
+
export const newLambdaProxyFromCloudFormationTemplate = <Context, AuthorizerContext extends APIGatewayAuthorizerResult, Event, HandlerResponse>(
|
|
12
14
|
name: string,
|
|
13
15
|
settings: HttpSettings,
|
|
14
16
|
payloadVersion: LambdaPayloadVersion<AuthorizerContext, Event, HandlerResponse>,
|
|
@@ -16,10 +18,13 @@ export const newLambdaProxyFromCloudFormationTemplate = <Context, AuthorizerCont
|
|
|
16
18
|
callback: Callback<string>,
|
|
17
19
|
): Startable<Service<string>> => new CloudFormationLambdaProxy(name, settings, config.extraRoutes, payloadVersion, config, callback);
|
|
18
20
|
|
|
19
|
-
class CloudFormationLambdaProxy<Context, AuthorizerContext, Event, HandlerResponse> extends CloudFormationSetup<
|
|
21
|
+
class CloudFormationLambdaProxy<Context, AuthorizerContext extends APIGatewayAuthorizerResult, Event, HandlerResponse> extends CloudFormationSetup<
|
|
20
22
|
{
|
|
21
23
|
Path: string;
|
|
22
24
|
Method: string;
|
|
25
|
+
Auth: {
|
|
26
|
+
Authorizer: string;
|
|
27
|
+
}
|
|
23
28
|
},
|
|
24
29
|
(event: Event) => Promise<HandlerResponse>,
|
|
25
30
|
Route<Context, AuthorizerContext, Event, HandlerResponse>,
|
|
@@ -47,7 +52,8 @@ class CloudFormationLambdaProxy<Context, AuthorizerContext, Event, HandlerRespon
|
|
|
47
52
|
path: new RegExp(`^${pathWithCapturingGroups}${QUERY_STRING_OR_LOCATION_REG_EXP}?$`),
|
|
48
53
|
weight: this.computeWeight(pathWithCapturingGroups),
|
|
49
54
|
handler: this.pathParameterCapture(new RegExp(pathWithCapturingGroups), handler, config.context),
|
|
50
|
-
|
|
55
|
+
authorizer: event.Properties?.Auth?.Authorizer === 'NONE' ? NOOP_AUTHORIZER : undefined,
|
|
56
|
+
} satisfies Route<Context, AuthorizerContext, Event, HandlerResponse>),
|
|
51
57
|
),
|
|
52
58
|
config.prepare,
|
|
53
59
|
config,
|
|
@@ -63,7 +69,7 @@ class CloudFormationLambdaProxy<Context, AuthorizerContext, Event, HandlerRespon
|
|
|
63
69
|
{
|
|
64
70
|
settings: this.settings,
|
|
65
71
|
routes: this.extraRoutes.concat(routes),
|
|
66
|
-
authorizer: (this.authorizer || (() =>
|
|
72
|
+
authorizer: (this.authorizer || (() => NOOP_AUTHORIZER))(cfg),
|
|
67
73
|
mapper: this.mapper,
|
|
68
74
|
},
|
|
69
75
|
this.callback,
|
|
@@ -117,7 +123,7 @@ class CloudFormationLambdaProxy<Context, AuthorizerContext, Event, HandlerRespon
|
|
|
117
123
|
}
|
|
118
124
|
}
|
|
119
125
|
|
|
120
|
-
type CloudFormationLambdaProxyConfig<Context, AuthorizerContext, Event, HandlerResponse> = {
|
|
126
|
+
type CloudFormationLambdaProxyConfig<Context, AuthorizerContext extends APIGatewayAuthorizerResult, Event, HandlerResponse> = {
|
|
121
127
|
authorizer?: (config: DevServerConfig) => Authorizer<AuthorizerContext> | undefined,
|
|
122
128
|
extraRoutes: Route<Context, AuthorizerContext, Event, HandlerResponse>[],
|
|
123
129
|
prepare?: (config: DevServerConfig) => Promise<void>,
|
package/src/cloudformation.ts
CHANGED
|
@@ -24,7 +24,7 @@ export abstract class CloudFormationSetup<P, H, R, F> implements Startable<F> {
|
|
|
24
24
|
this.handlerNameResolver = config.handlerNameResolver;
|
|
25
25
|
}
|
|
26
26
|
start = async (config: DevServerConfig) =>
|
|
27
|
-
(this.beforeStart ||
|
|
27
|
+
(this.beforeStart || (async () => undefined))(config)
|
|
28
28
|
.then(this.resolvedHandlers)
|
|
29
29
|
.then(async (routes) => this.afterStart(this.name, config, routes));
|
|
30
30
|
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { DevServer } from './dev-server'
|
|
2
2
|
export { newHttpServer as httpServer, HttpSettings } from './http-server'
|
|
3
|
-
export { newLambdaHttpProxy as lambdaHttpProxy
|
|
3
|
+
export { newLambdaHttpProxy as lambdaHttpProxy } from './lambda-http-proxy'
|
|
4
4
|
export { v1 as v1LambdaProxyPayload, v2 as v2LambdaProxyPayload } from './mappers'
|
|
5
5
|
export { newLambdaProxyFromCloudFormationTemplate as cloudFormationLambdaProxy } from './cloudformation-lambda-http-proxy'
|
|
6
6
|
export { newDynamoDbTableFromCloudFormationTemplate as cloudFormationDynamoDbTable } from './cloudformation-dynamodb-table'
|
package/src/lambda-http-proxy.ts
CHANGED
|
@@ -4,9 +4,10 @@ import { IncomingMessage, ServerResponse } from "http";
|
|
|
4
4
|
import { LambdaMapperFactory } from "./mappers";
|
|
5
5
|
import { internalServerError, writeResponse } from "./responses";
|
|
6
6
|
import { Lifecycle, Service, Callback } from "./services";
|
|
7
|
+
import { APIGatewayAuthorizerResult } from "aws-lambda";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
export const newLambdaHttpProxy = <Context, AuthorizerContext, Event, HandlerResponse>(
|
|
9
|
+
const UNAUTHORIZED = new Error('UNAUTHORIZED');
|
|
10
|
+
export const newLambdaHttpProxy = <Context, AuthorizerContext extends APIGatewayAuthorizerResult, Event, HandlerResponse>(
|
|
10
11
|
name: string,
|
|
11
12
|
config: {
|
|
12
13
|
settings: HttpSettings,
|
|
@@ -25,7 +26,7 @@ export const newLambdaHttpProxy = <Context, AuthorizerContext, Event, HandlerRes
|
|
|
25
26
|
config.context,
|
|
26
27
|
), callback)
|
|
27
28
|
|
|
28
|
-
class LambdaHttpProxy<Context, AuthorizerContext, Event, HandlerResponse> implements Lifecycle<string> {
|
|
29
|
+
class LambdaHttpProxy<Context, AuthorizerContext extends APIGatewayAuthorizerResult, Event, HandlerResponse> implements Lifecycle<string> {
|
|
29
30
|
private LOGGER: Logger
|
|
30
31
|
private readonly server: HttpServer
|
|
31
32
|
|
|
@@ -55,6 +56,11 @@ class LambdaHttpProxy<Context, AuthorizerContext, Event, HandlerResponse> implem
|
|
|
55
56
|
.then(async (mapper) =>
|
|
56
57
|
authorizer(mapper.authorization())
|
|
57
58
|
.then(async (context) =>
|
|
59
|
+
context?.policyDocument?.Statement?.find((s) => s.Effect === 'Deny') === undefined
|
|
60
|
+
? { ...context, lambda: context?.context }
|
|
61
|
+
: Promise.reject(UNAUTHORIZED),
|
|
62
|
+
)
|
|
63
|
+
.then((context) =>
|
|
58
64
|
lambdaHandler(mapper.event(context), this.context())
|
|
59
65
|
.then((lambda) => {
|
|
60
66
|
const result = mapper.toResponse(lambda)
|
|
@@ -100,7 +106,7 @@ export abstract class Response {
|
|
|
100
106
|
body: () => string | Buffer | undefined
|
|
101
107
|
}
|
|
102
108
|
|
|
103
|
-
export type Route<Context, AuthorizerContext, Event, HandlerResponse> = {
|
|
109
|
+
export type Route<Context, AuthorizerContext extends APIGatewayAuthorizerResult, Event, HandlerResponse> = {
|
|
104
110
|
method: RegExp;
|
|
105
111
|
path: RegExp;
|
|
106
112
|
weight: number;
|
|
@@ -108,4 +114,4 @@ export type Route<Context, AuthorizerContext, Event, HandlerResponse> = {
|
|
|
108
114
|
handler: (event: Event, context: Context) => Promise<HandlerResponse>;
|
|
109
115
|
};
|
|
110
116
|
|
|
111
|
-
export type Authorizer<Context> = (authorization: string) => Promise<Context | undefined>;
|
|
117
|
+
export type Authorizer<Context extends APIGatewayAuthorizerResult> = (authorization: string) => Promise<Context | undefined>;
|
package/src/mappers.ts
CHANGED
|
@@ -120,7 +120,7 @@ class DefaultLambdaMapper<AuthorizerContext> implements LambdaMapper<AuthorizerC
|
|
|
120
120
|
return [];
|
|
121
121
|
};
|
|
122
122
|
|
|
123
|
-
event = (context: AuthorizerContext): APIGatewayProxyEvent & APIGatewayEventRequestContextV2WithGenericAuthorizer<AuthorizerContext> => ({
|
|
123
|
+
event = (context: { [name: string]: any; } & AuthorizerContext): APIGatewayProxyEvent & APIGatewayEventRequestContextV2WithGenericAuthorizer<AuthorizerContext> => ({
|
|
124
124
|
version: '2.0',
|
|
125
125
|
rawPath: this.url.pathname,
|
|
126
126
|
rawQueryString: this.url.search,
|
|
@@ -190,7 +190,7 @@ class DefaultLambdaMapper<AuthorizerContext> implements LambdaMapper<AuthorizerC
|
|
|
190
190
|
authorization = () => this.authorizationHeaderValue
|
|
191
191
|
}
|
|
192
192
|
interface LambdaMapper<AuthorizerContext, Event, HandlerResponse> {
|
|
193
|
-
event(context: AuthorizerContext): Event
|
|
193
|
+
event(context: { [name: string]: any; } & AuthorizerContext): Event
|
|
194
194
|
authorization(): string
|
|
195
195
|
toResponse: (response: HandlerResponse) => Response
|
|
196
196
|
}
|