aws-classify-server 0.1.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.
- package/LICENSE +21 -0
- package/README.md +306 -0
- package/lib/cjs/ClassDef.d.ts +6 -0
- package/lib/cjs/ClassDef.js +2 -0
- package/lib/cjs/ClassifyResponse.d.ts +6 -0
- package/lib/cjs/ClassifyResponse.js +11 -0
- package/lib/cjs/ClassifyServerless.d.ts +27 -0
- package/lib/cjs/ClassifyServerless.js +288 -0
- package/lib/cjs/SessionData.d.ts +6 -0
- package/lib/cjs/SessionData.js +2 -0
- package/lib/cjs/aws-classify-common/EndPointsLogging.d.ts +8 -0
- package/lib/cjs/aws-classify-common/EndPointsLogging.js +11 -0
- package/lib/cjs/aws-classify-common/LambdaRequest.d.ts +6 -0
- package/lib/cjs/aws-classify-common/LambdaRequest.js +2 -0
- package/lib/cjs/aws-classify-common/LambdaResponse.d.ts +6 -0
- package/lib/cjs/aws-classify-common/LambdaResponse.js +2 -0
- package/lib/cjs/aws-classify-common/index.d.ts +4 -0
- package/lib/cjs/aws-classify-common/index.js +7 -0
- package/lib/cjs/dispatch.d.ts +7 -0
- package/lib/cjs/dispatch.js +79 -0
- package/lib/cjs/index.d.ts +3 -0
- package/lib/cjs/index.js +12 -0
- package/lib/esm/ClassDef.d.ts +6 -0
- package/lib/esm/ClassDef.js +1 -0
- package/lib/esm/ClassifyResponse.d.ts +6 -0
- package/lib/esm/ClassifyResponse.js +7 -0
- package/lib/esm/ClassifyServerless.d.ts +27 -0
- package/lib/esm/ClassifyServerless.js +282 -0
- package/lib/esm/SessionData.d.ts +6 -0
- package/lib/esm/SessionData.js +1 -0
- package/lib/esm/aws-classify-common/EndPointsLogging.d.ts +8 -0
- package/lib/esm/aws-classify-common/EndPointsLogging.js +8 -0
- package/lib/esm/aws-classify-common/LambdaRequest.d.ts +6 -0
- package/lib/esm/aws-classify-common/LambdaRequest.js +1 -0
- package/lib/esm/aws-classify-common/LambdaResponse.d.ts +6 -0
- package/lib/esm/aws-classify-common/LambdaResponse.js +1 -0
- package/lib/esm/aws-classify-common/index.d.ts +4 -0
- package/lib/esm/aws-classify-common/index.js +2 -0
- package/lib/esm/dispatch.d.ts +7 -0
- package/lib/esm/dispatch.js +73 -0
- package/lib/esm/index.d.ts +3 -0
- package/lib/esm/index.js +3 -0
- package/package.json +36 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reqBody = exports.EndPointsLogging = void 0;
|
|
4
|
+
var EndPointsLogging_1 = require("./EndPointsLogging");
|
|
5
|
+
Object.defineProperty(exports, "EndPointsLogging", { enumerable: true, get: function () { return EndPointsLogging_1.EndPointsLogging; } });
|
|
6
|
+
const reqBody = () => { throw new Error('Request class not registered'); };
|
|
7
|
+
exports.reqBody = reqBody;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { APIGatewayProxyEvent, Context } from 'aws-lambda';
|
|
2
|
+
import { ClassifyServerless } from "./index";
|
|
3
|
+
import { APIGatewayProxyStructuredResultV2 } from "aws-lambda/trigger/api-gateway-proxy";
|
|
4
|
+
export declare const classifyServerless: ClassifyServerless;
|
|
5
|
+
export declare const responseHandler: (event: APIGatewayProxyEvent, context: Context) => Promise<APIGatewayProxyStructuredResultV2>;
|
|
6
|
+
export declare const webSocketConnect: (event: APIGatewayProxyEvent, _context: Context) => Promise<APIGatewayProxyStructuredResultV2>;
|
|
7
|
+
export declare const webSocketDisconnect: (_event: APIGatewayProxyEvent, _context: Context) => Promise<APIGatewayProxyStructuredResultV2>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.webSocketDisconnect = exports.webSocketConnect = exports.responseHandler = exports.classifyServerless = void 0;
|
|
13
|
+
const index_1 = require("./index");
|
|
14
|
+
const js_freeze_dry_1 = require("js-freeze-dry");
|
|
15
|
+
const ClassifyServerless_1 = require("./ClassifyServerless");
|
|
16
|
+
exports.classifyServerless = new index_1.ClassifyServerless();
|
|
17
|
+
const responseHandler = (event, context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
|
+
try {
|
|
19
|
+
//console.log(`dispatching`);
|
|
20
|
+
return {
|
|
21
|
+
statusCode: 200,
|
|
22
|
+
body: (yield exports.classifyServerless.dispatch(event, context)) || "",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
//console.log(`request.body = ${event.body}`);
|
|
27
|
+
console.log(`error = ${err.message} ${err.stack}`);
|
|
28
|
+
return {
|
|
29
|
+
statusCode: 200,
|
|
30
|
+
body: (0, js_freeze_dry_1.serialize)({
|
|
31
|
+
data: undefined,
|
|
32
|
+
exception: `Internal Server Error (${err}) - see log at ${new Date()}`,
|
|
33
|
+
cargo: undefined,
|
|
34
|
+
sessionId: undefined
|
|
35
|
+
})
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
exports.responseHandler = responseHandler;
|
|
40
|
+
const webSocketConnect = (event, _context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
41
|
+
//console.log('connecting');
|
|
42
|
+
const sessionId = event.headers['Sec-WebSocket-Protocol'] || "";
|
|
43
|
+
const connectId = event.requestContext.connectionId;
|
|
44
|
+
//console.log(JSON.stringify(event));
|
|
45
|
+
const result = yield (0, ClassifyServerless_1.getSessionData)(sessionId); // Make sure session id passed in is valid
|
|
46
|
+
if (result) {
|
|
47
|
+
// Save connection id
|
|
48
|
+
yield (0, ClassifyServerless_1.saveSessionData)(sessionId, undefined, undefined, connectId);
|
|
49
|
+
//console.log(`webSocketConnect session ${sessionId} connected to websocket connectId ${connectId}`);
|
|
50
|
+
return {
|
|
51
|
+
statusCode: 200,
|
|
52
|
+
headers: {
|
|
53
|
+
'Sec-WebSocket-Protocol': sessionId
|
|
54
|
+
},
|
|
55
|
+
body: JSON.stringify({
|
|
56
|
+
msg: connectId,
|
|
57
|
+
})
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.log(`invalid sessionId - webSocketConnect sessionId=${sessionId} connectId=${connectId}`);
|
|
62
|
+
return {
|
|
63
|
+
statusCode: 500,
|
|
64
|
+
body: 'Opps'
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
exports.webSocketConnect = webSocketConnect;
|
|
69
|
+
const webSocketDisconnect = (_event, _context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
70
|
+
//console.log('Disconnect ' + JSON.stringify(event));
|
|
71
|
+
//console.log(`webSocketDisconnect sessionId=${sessionId} connectId=${connectId}`);
|
|
72
|
+
return {
|
|
73
|
+
statusCode: 200,
|
|
74
|
+
headers: {
|
|
75
|
+
'Sec-WebSocket-Protocol': 'websocket'
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
exports.webSocketDisconnect = webSocketDisconnect;
|
package/lib/cjs/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.responseHandler = exports.webSocketDisconnect = exports.webSocketConnect = exports.classifyServerless = exports.ClassifyResponse = exports.ClassifyServerless = void 0;
|
|
4
|
+
var ClassifyServerless_1 = require("./ClassifyServerless");
|
|
5
|
+
Object.defineProperty(exports, "ClassifyServerless", { enumerable: true, get: function () { return ClassifyServerless_1.ClassifyServerless; } });
|
|
6
|
+
var ClassifyResponse_1 = require("./ClassifyResponse");
|
|
7
|
+
Object.defineProperty(exports, "ClassifyResponse", { enumerable: true, get: function () { return ClassifyResponse_1.ClassifyResponse; } });
|
|
8
|
+
var dispatch_1 = require("./dispatch");
|
|
9
|
+
Object.defineProperty(exports, "classifyServerless", { enumerable: true, get: function () { return dispatch_1.classifyServerless; } });
|
|
10
|
+
Object.defineProperty(exports, "webSocketConnect", { enumerable: true, get: function () { return dispatch_1.webSocketConnect; } });
|
|
11
|
+
Object.defineProperty(exports, "webSocketDisconnect", { enumerable: true, get: function () { return dispatch_1.webSocketDisconnect; } });
|
|
12
|
+
Object.defineProperty(exports, "responseHandler", { enumerable: true, get: function () { return dispatch_1.responseHandler; } });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ClassifyResponse } from "./ClassifyResponse";
|
|
2
|
+
export interface ClassDef<ServerClass extends ClassifyResponse, ClientClass> {
|
|
3
|
+
serverClass: new () => ServerClass;
|
|
4
|
+
clientClass: new () => ClientClass;
|
|
5
|
+
authorizer: ((endPoint: ServerClass, method: string, args: IArguments) => Promise<boolean>) | undefined;
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ClassDef } from "./ClassDef";
|
|
2
|
+
import { APIGatewayProxyEvent, Context } from "aws-lambda";
|
|
3
|
+
export declare class ClassifyServerless {
|
|
4
|
+
logLevel: Partial<typeof EndPointsLogging>;
|
|
5
|
+
classDefs: Map<any, ClassDef<any, any>>;
|
|
6
|
+
requests: Map<any, any>;
|
|
7
|
+
log: (_message: string) => void;
|
|
8
|
+
setLogger(log: (_message: string) => void): void;
|
|
9
|
+
setLogLevel(logLevel: Partial<typeof EndPointsLogging>): void;
|
|
10
|
+
registerResponse<T>(responseClass: new () => T, authorizer?: (_endPoint: T, _method: string, _args: IArguments) => Promise<boolean>): void;
|
|
11
|
+
dispatch(ev: APIGatewayProxyEvent, context: Context, classes?: {}): Promise<string>;
|
|
12
|
+
registerRequest<T>(requestClass: new () => T, classes?: any): new () => T;
|
|
13
|
+
getSessionId<T>(responseObj: T): any;
|
|
14
|
+
createRequest<T, U>(responseObj: U, requestClass: new () => T): T;
|
|
15
|
+
createRequestForSession<T>(sessionId: string, requestClass: new () => T): Promise<T>;
|
|
16
|
+
createResponse<T, R>(responseClass: new () => T, sessionId: string, callback: (_obj: T) => Promise<R>, classes?: any): Promise<R>;
|
|
17
|
+
}
|
|
18
|
+
export declare function getSessionData(sessionId: string, interfaceName?: string): Promise<Record<string, any> | undefined>;
|
|
19
|
+
export declare function saveSessionData(sessionId: string, interfaceName?: string, sessionData?: string, connectionId?: string): Promise<string>;
|
|
20
|
+
export declare const EndPointsLogging: {
|
|
21
|
+
create: boolean;
|
|
22
|
+
connect: boolean;
|
|
23
|
+
exceptions: boolean;
|
|
24
|
+
calls: boolean;
|
|
25
|
+
requests: boolean;
|
|
26
|
+
data: boolean;
|
|
27
|
+
};
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { deserialize, serialize } from "js-freeze-dry";
|
|
11
|
+
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";
|
|
12
|
+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
13
|
+
import { ApiGatewayManagementApiClient, PostToConnectionCommand } from "@aws-sdk/client-apigatewaymanagementapi";
|
|
14
|
+
const ddbClient = new DynamoDBClient({ region: process.env.DD_REGION, endpoint: process.env.DD_ENDPOINT });
|
|
15
|
+
const ddbDocClient = DynamoDBDocument.from(ddbClient);
|
|
16
|
+
export class ClassifyServerless {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.logLevel = {};
|
|
19
|
+
this.classDefs = new Map();
|
|
20
|
+
this.requests = new Map();
|
|
21
|
+
this.log = msg => console.log(msg);
|
|
22
|
+
}
|
|
23
|
+
setLogger(log) {
|
|
24
|
+
this.log = log;
|
|
25
|
+
}
|
|
26
|
+
setLogLevel(logLevel) {
|
|
27
|
+
this.logLevel = logLevel;
|
|
28
|
+
}
|
|
29
|
+
registerResponse(responseClass, authorizer) {
|
|
30
|
+
// Find ultimate base class
|
|
31
|
+
let clientClass = responseClass;
|
|
32
|
+
while (Object.getPrototypeOf(clientClass).prototype)
|
|
33
|
+
clientClass = Object.getPrototypeOf(clientClass);
|
|
34
|
+
if (clientClass === responseClass)
|
|
35
|
+
throw ('ClassifyServerless.registerResponse: Response class must extend a request class');
|
|
36
|
+
// Get interface name as static property of request class
|
|
37
|
+
const interfaceName = clientClass['interfaceName'];
|
|
38
|
+
if (!interfaceName)
|
|
39
|
+
throw ('ClassifyServerless.registerResponse: Request must have interfaceName as a static property');
|
|
40
|
+
this.classDefs.set(interfaceName, { serverClass: responseClass, clientClass, authorizer });
|
|
41
|
+
responseClass.__interfaceName__ = interfaceName;
|
|
42
|
+
}
|
|
43
|
+
dispatch(ev, context, classes = {}) {
|
|
44
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
const request = deserialize(ev.body, classes);
|
|
46
|
+
const logName = `${request.interfaceName}.${request.methodName}`;
|
|
47
|
+
let sessionId = request.sessionId; // May be empty
|
|
48
|
+
// Pseudo interface/method to request socket access and return a session id to use in connect
|
|
49
|
+
// session id will be passed in sec-websocket-protocol on connect request
|
|
50
|
+
if (logName === '$WebSocket.$authorize') {
|
|
51
|
+
const socketResult = yield getSessionData(sessionId, "");
|
|
52
|
+
if (!socketResult) {
|
|
53
|
+
sessionId = context.awsRequestId;
|
|
54
|
+
yield saveSessionData(sessionId, undefined, undefined, undefined);
|
|
55
|
+
}
|
|
56
|
+
const lambdaResponse = {
|
|
57
|
+
data: process.env.IS_OFFLINE ? 'ws://localhost:3001' : `wss://${process.env.APIG_ENDPOINT}`,
|
|
58
|
+
exception: undefined,
|
|
59
|
+
cargo: undefined,
|
|
60
|
+
sessionId: sessionId,
|
|
61
|
+
};
|
|
62
|
+
return serialize(lambdaResponse, classes);
|
|
63
|
+
}
|
|
64
|
+
if (this.logLevel.data && this.logLevel.calls)
|
|
65
|
+
this.log(`Lambda ${logName} invoked with ${ev.body}`);
|
|
66
|
+
else if (this.logLevel.calls)
|
|
67
|
+
this.log(`Lambda ${logName} invoked`);
|
|
68
|
+
const classDef = this.classDefs.get(request.interfaceName);
|
|
69
|
+
if (!classDef)
|
|
70
|
+
throw new Error(`No Response defined for ${request.interfaceName}`);
|
|
71
|
+
if (!classDef.clientClass.prototype[request.methodName])
|
|
72
|
+
throw new Error(`${request.methodName} not found in ${request.interfaceName} client request class`);
|
|
73
|
+
// If authorizer passed in make sure request is authorized
|
|
74
|
+
if (!classDef.authorizer || (yield classDef.authorizer(request.interfaceName, request.methodName, request.args))) {
|
|
75
|
+
// Instantiate the class
|
|
76
|
+
const obj = new classDef.serverClass();
|
|
77
|
+
// Retrieve session data from DynamoDB base on sessionId in request
|
|
78
|
+
const result = yield getSessionData(sessionId, request.interfaceName);
|
|
79
|
+
if (!result)
|
|
80
|
+
sessionId = context.awsRequestId;
|
|
81
|
+
else {
|
|
82
|
+
const sessionData = result[`interface_${request.interfaceName}`];
|
|
83
|
+
if (sessionData) // Enrich it with session data
|
|
84
|
+
Object.assign(obj, deserialize(sessionData, classes));
|
|
85
|
+
}
|
|
86
|
+
obj.__sessionId__ = sessionId;
|
|
87
|
+
obj.__connectionId__ = result === null || result === void 0 ? void 0 : result.connectionId;
|
|
88
|
+
// Call the class method and catch any exceptions it might throw to pass back
|
|
89
|
+
let fnResponse;
|
|
90
|
+
try {
|
|
91
|
+
fnResponse = yield obj[request.methodName].call(obj, ...request.args);
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
if (this.logLevel.calls)
|
|
95
|
+
this.log(`Lambda ${logName} had exception ${e}`);
|
|
96
|
+
const lambdaResponse = {
|
|
97
|
+
data: undefined,
|
|
98
|
+
exception: e.message,
|
|
99
|
+
cargo: undefined,
|
|
100
|
+
sessionId: sessionId,
|
|
101
|
+
};
|
|
102
|
+
return serialize(lambdaResponse, classes);
|
|
103
|
+
}
|
|
104
|
+
const updatedSessionData = serialize(obj, classes);
|
|
105
|
+
if (!result || updatedSessionData !== result.sessionData)
|
|
106
|
+
sessionId = yield saveSessionData(sessionId, request.interfaceName, updatedSessionData, undefined);
|
|
107
|
+
// Formulate response
|
|
108
|
+
const lambdaResponse = {
|
|
109
|
+
data: fnResponse,
|
|
110
|
+
exception: undefined,
|
|
111
|
+
cargo: undefined,
|
|
112
|
+
sessionId,
|
|
113
|
+
};
|
|
114
|
+
const response = serialize(lambdaResponse, classes);
|
|
115
|
+
if (this.logLevel.data && this.logLevel.calls)
|
|
116
|
+
this.log(`Lambda ${logName} responded with ${response}`);
|
|
117
|
+
else if (this.logLevel.calls)
|
|
118
|
+
this.log(`Lambda ${logName} responded`);
|
|
119
|
+
return response;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
this.log(`Lambda ${logName} not authorized`);
|
|
123
|
+
throw new Error("Not Authorized");
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
registerRequest(requestClass, classes = {}) {
|
|
128
|
+
const interfaceName = requestClass['interfaceName'];
|
|
129
|
+
if (!interfaceName)
|
|
130
|
+
throw ('ClassifyServerless.registerRequest: Request must have interfaceName as a static property');
|
|
131
|
+
for (const methodName of Object.getOwnPropertyNames(requestClass.prototype)) {
|
|
132
|
+
if (methodName === 'constructor' || typeof (requestClass.prototype)[methodName] !== 'function')
|
|
133
|
+
continue;
|
|
134
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
135
|
+
const expressServer = this;
|
|
136
|
+
requestClass.prototype[methodName] = function (...args) {
|
|
137
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
try {
|
|
139
|
+
// Build request
|
|
140
|
+
const sessionId = this['__sessionId__'];
|
|
141
|
+
const connectionId = this['__connectionId__'];
|
|
142
|
+
if (!sessionId)
|
|
143
|
+
throw new Error(`You must instantiate ${interfaceName} using the ClassifyServerless.createRequest`);
|
|
144
|
+
if (!connectionId)
|
|
145
|
+
throw new Error(`WebSocket handshake not established for ${interfaceName}`);
|
|
146
|
+
const request = {
|
|
147
|
+
interfaceName: interfaceName,
|
|
148
|
+
args, methodName,
|
|
149
|
+
sessionId
|
|
150
|
+
};
|
|
151
|
+
const payload = serialize(request, classes);
|
|
152
|
+
// Log request
|
|
153
|
+
if (expressServer.logLevel.data && expressServer.logLevel.requests)
|
|
154
|
+
expressServer.log(`Request ${interfaceName} emitting with ${payload}`);
|
|
155
|
+
else if (expressServer.logLevel && expressServer.logLevel.requests)
|
|
156
|
+
expressServer.log(`Request ${interfaceName} emitting`);
|
|
157
|
+
// Post request to the gateway to be sent to src
|
|
158
|
+
const endpoint = process.env.IS_OFFLINE ? 'http://localhost:3001' : `https://${process.env.APIG_ENDPOINT}`;
|
|
159
|
+
if (expressServer.logLevel.calls)
|
|
160
|
+
expressServer.log(`creating APIGatewayManagementApiClient for endpoint ${endpoint}`);
|
|
161
|
+
const client = new ApiGatewayManagementApiClient({ endpoint });
|
|
162
|
+
if (expressServer.logLevel.data)
|
|
163
|
+
expressServer.log(`posting to ${connectionId} ${payload}`);
|
|
164
|
+
const post = new PostToConnectionCommand({
|
|
165
|
+
ConnectionId: connectionId,
|
|
166
|
+
Data: Buffer.from(payload)
|
|
167
|
+
});
|
|
168
|
+
yield client.send(post);
|
|
169
|
+
// Catch any exception, so it can be logged and then rethrown
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
if (expressServer.logLevel.exceptions)
|
|
173
|
+
expressServer.log(e.message);
|
|
174
|
+
throw e;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
if (this.logLevel.create)
|
|
179
|
+
this.log(`${interfaceName} bound to ${requestClass.name}.${methodName}`);
|
|
180
|
+
}
|
|
181
|
+
return requestClass;
|
|
182
|
+
}
|
|
183
|
+
getSessionId(responseObj) {
|
|
184
|
+
const sessionId = responseObj['__sessionId__'];
|
|
185
|
+
if (!sessionId)
|
|
186
|
+
throw Error('ClassifyServerless.getSessionId: no session id');
|
|
187
|
+
return sessionId;
|
|
188
|
+
}
|
|
189
|
+
createRequest(responseObj, requestClass) {
|
|
190
|
+
const obj = new requestClass();
|
|
191
|
+
obj['__sessionId__'] = responseObj['__sessionId__'];
|
|
192
|
+
obj['__connectionId__'] = responseObj['__connectionId__'];
|
|
193
|
+
return obj;
|
|
194
|
+
}
|
|
195
|
+
createRequestForSession(sessionId, requestClass) {
|
|
196
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
197
|
+
// Get connection Id
|
|
198
|
+
const result = yield getSessionData(sessionId);
|
|
199
|
+
if (!result)
|
|
200
|
+
throw new Error(`ClassifyResponse.createRequestForSession: invalid session id`);
|
|
201
|
+
if (!result.connectionId)
|
|
202
|
+
throw new Error(`WebSocket handshake not established for session`);
|
|
203
|
+
// Create request
|
|
204
|
+
const obj = new requestClass();
|
|
205
|
+
obj['__sessionId__'] = sessionId;
|
|
206
|
+
obj['__connectionId__'] = result.connectionId;
|
|
207
|
+
return obj;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
createResponse(responseClass, sessionId, callback, classes = {}) {
|
|
211
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
212
|
+
const interfaceName = responseClass.__interfaceName__;
|
|
213
|
+
if (!interfaceName)
|
|
214
|
+
throw new Error(`ClassifyResponse.createResponse: Failed to call registerResponse for ${responseClass.name}`);
|
|
215
|
+
const result = yield getSessionData(sessionId, interfaceName);
|
|
216
|
+
if (!result)
|
|
217
|
+
throw new Error(`ClassifyResponse.createResponse: invalid session id for interface ${interfaceName}`);
|
|
218
|
+
const sessionData = result[`interface_${interfaceName}`];
|
|
219
|
+
// Instantiate the class
|
|
220
|
+
const obj = (new responseClass());
|
|
221
|
+
// Enrich it with session data
|
|
222
|
+
if (sessionData)
|
|
223
|
+
Object.assign(obj, deserialize(sessionData, classes));
|
|
224
|
+
obj.__sessionId__ = sessionId;
|
|
225
|
+
obj.__connectionId__ = result.connectionId;
|
|
226
|
+
if (this.logLevel.calls)
|
|
227
|
+
this.log(`creating response for ${interfaceName} with sessionId=${sessionId} connectionId= ${obj.__connectionId__} sessionData=${sessionData}`);
|
|
228
|
+
const ret = yield callback(obj);
|
|
229
|
+
const updatedSessionData = serialize(obj, classes);
|
|
230
|
+
if (updatedSessionData !== result.sessionData) {
|
|
231
|
+
if (this.logLevel.calls)
|
|
232
|
+
this.log(`saving session data for ${interfaceName} with sessionId=${sessionId} connectionId= ${obj.__connectionId__} sessionData=${updatedSessionData}`);
|
|
233
|
+
yield saveSessionData(sessionId, interfaceName, updatedSessionData, undefined);
|
|
234
|
+
}
|
|
235
|
+
return ret;
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
export function getSessionData(sessionId, interfaceName = "") {
|
|
240
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
241
|
+
if (sessionId) {
|
|
242
|
+
const data = yield ddbDocClient.get({
|
|
243
|
+
TableName: 'classifySessionStore',
|
|
244
|
+
Key: { sessionId },
|
|
245
|
+
ConsistentRead: true,
|
|
246
|
+
ProjectionExpression: `interface_${interfaceName}, connectionId, updated`
|
|
247
|
+
});
|
|
248
|
+
return data.Item;
|
|
249
|
+
}
|
|
250
|
+
return undefined;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
export function saveSessionData(sessionId, interfaceName, sessionData, connectionId) {
|
|
254
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
255
|
+
const updateExpressionComponents = ['updated = :time'];
|
|
256
|
+
const expressionAttributeValues = { ':time': new Date().getTime() };
|
|
257
|
+
if (sessionData && interfaceName) {
|
|
258
|
+
updateExpressionComponents.push(`interface_${interfaceName} = :data`);
|
|
259
|
+
expressionAttributeValues[`:data`] = sessionData;
|
|
260
|
+
}
|
|
261
|
+
if (connectionId) {
|
|
262
|
+
updateExpressionComponents.push('connectionId = :connectionId');
|
|
263
|
+
expressionAttributeValues[':connectionId'] = connectionId;
|
|
264
|
+
}
|
|
265
|
+
// First try and out data only if the session id exists
|
|
266
|
+
yield ddbDocClient.update({
|
|
267
|
+
TableName: 'classifySessionStore',
|
|
268
|
+
Key: { sessionId },
|
|
269
|
+
UpdateExpression: `set ${updateExpressionComponents.join(", ")}`,
|
|
270
|
+
ExpressionAttributeValues: expressionAttributeValues
|
|
271
|
+
});
|
|
272
|
+
return sessionId;
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
export const EndPointsLogging = {
|
|
276
|
+
create: true,
|
|
277
|
+
connect: true,
|
|
278
|
+
exceptions: true,
|
|
279
|
+
calls: true,
|
|
280
|
+
requests: false,
|
|
281
|
+
data: false,
|
|
282
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { APIGatewayProxyEvent, Context } from 'aws-lambda';
|
|
2
|
+
import { ClassifyServerless } from "./index";
|
|
3
|
+
import { APIGatewayProxyStructuredResultV2 } from "aws-lambda/trigger/api-gateway-proxy";
|
|
4
|
+
export declare const classifyServerless: ClassifyServerless;
|
|
5
|
+
export declare const responseHandler: (event: APIGatewayProxyEvent, context: Context) => Promise<APIGatewayProxyStructuredResultV2>;
|
|
6
|
+
export declare const webSocketConnect: (event: APIGatewayProxyEvent, _context: Context) => Promise<APIGatewayProxyStructuredResultV2>;
|
|
7
|
+
export declare const webSocketDisconnect: (_event: APIGatewayProxyEvent, _context: Context) => Promise<APIGatewayProxyStructuredResultV2>;
|