somod-chat-service 0.8.2 → 1.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.
@@ -1,34 +1,32 @@
1
- import { __awaiter, __generator } from "tslib";
2
- export var cache = function (max, ttl) {
3
- var cacheTtl = ttl;
4
- var nodes = {};
5
- var first = undefined;
6
- var last = undefined;
7
- var size = 0;
8
- var remove = function (key) {
9
- var _a, _b;
10
- var node = nodes[key];
1
+ export const cache = (max, ttl) => {
2
+ const cacheTtl = ttl;
3
+ const nodes = {};
4
+ let first = undefined;
5
+ let last = undefined;
6
+ let size = 0;
7
+ const remove = (key) => {
8
+ const node = nodes[key];
11
9
  if (node.next) {
12
10
  node.next.previous = node.previous;
13
11
  }
14
12
  else {
15
- last = (_a = node.previous) === null || _a === void 0 ? void 0 : _a.content.key;
13
+ last = node.previous?.content.key;
16
14
  }
17
15
  if (node.previous) {
18
16
  node.previous.next = node.next;
19
17
  }
20
18
  else {
21
- first = (_b = node.next) === null || _b === void 0 ? void 0 : _b.content.key;
19
+ first = node.next?.content.key;
22
20
  }
23
21
  size--;
24
22
  delete nodes[key];
25
23
  };
26
- var add = function (key, value) {
27
- var node = {
24
+ const add = (key, value) => {
25
+ const node = {
28
26
  content: {
29
- key: key,
27
+ key,
30
28
  cachedAt: Date.now(),
31
- value: value
29
+ value
32
30
  }
33
31
  };
34
32
  nodes[key] = node;
@@ -39,34 +37,25 @@ export var cache = function (max, ttl) {
39
37
  }
40
38
  first = key;
41
39
  };
42
- var get = function (key, fetcher, ttl) { return __awaiter(void 0, void 0, void 0, function () {
43
- var node, value, now;
44
- return __generator(this, function (_a) {
45
- switch (_a.label) {
46
- case 0:
47
- node = nodes[key];
48
- value = node === null || node === void 0 ? void 0 : node.content.value;
49
- now = Date.now();
50
- if (!(node === undefined ||
51
- (ttl && node.content.cachedAt < now - ttl) ||
52
- (cacheTtl && node.content.cachedAt < now - cacheTtl))) return [3 /*break*/, 2];
53
- return [4 /*yield*/, fetcher()];
54
- case 1:
55
- value = _a.sent();
56
- _a.label = 2;
57
- case 2:
58
- if (node) {
59
- remove(node.content.key);
60
- }
61
- add(key, value);
62
- if (size > max && last) {
63
- remove(last);
64
- }
65
- return [2 /*return*/, value];
66
- }
67
- });
68
- }); };
40
+ const get = async (key, fetcher, ttl) => {
41
+ const node = nodes[key];
42
+ let value = node?.content.value;
43
+ const now = Date.now();
44
+ if (node === undefined ||
45
+ (ttl && node.content.cachedAt < now - ttl) ||
46
+ (cacheTtl && node.content.cachedAt < now - cacheTtl)) {
47
+ value = await fetcher();
48
+ }
49
+ if (node) {
50
+ remove(node.content.key);
51
+ }
52
+ add(key, value);
53
+ if (size > max && last) {
54
+ remove(last);
55
+ }
56
+ return value;
57
+ };
69
58
  return {
70
- get: get
59
+ get
71
60
  };
72
61
  };
@@ -1 +1 @@
1
- export var UserProviderMiddlewareKey = "chat-backend-user-id";
1
+ export const UserProviderMiddlewareKey = "chat-backend-user-id";
@@ -1,5 +1,5 @@
1
1
  import { UserProviderMiddlewareKey } from "./constants";
2
2
  // get User Id from Event
3
- export var getUserIdFromEvent = function (event) {
3
+ export const getUserIdFromEvent = (event) => {
4
4
  return event.somodMiddlewareContext.get(UserProviderMiddlewareKey);
5
5
  };
@@ -1,61 +1,46 @@
1
- import { __assign, __awaiter, __generator } from "tslib";
2
1
  import { marshall } from "@aws-sdk/util-dynamodb";
3
2
  import { typeToAllowedActionsMap } from "./types";
4
3
  import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
5
4
  import { threadCache } from "./threadCache";
6
- var dynamodb = new DynamoDBClient();
7
- export var putMessage = function (tableName, userId, message) { return __awaiter(void 0, void 0, void 0, function () {
8
- var msg, messageWithKeys, putItemCommand;
9
- return __generator(this, function (_a) {
10
- switch (_a.label) {
11
- case 0:
12
- msg = __assign(__assign({}, message), { seqNo: Date.now() });
13
- messageWithKeys = __assign(__assign({}, msg), { userId: userId });
14
- putItemCommand = new PutItemCommand({
15
- TableName: tableName,
16
- Item: marshall(messageWithKeys),
17
- ConditionExpression: "attribute_not_exists(#userId) AND attribute_not_exists(#seqNo)",
18
- ExpressionAttributeNames: {
19
- "#userId": "userId",
20
- "#seqNo": "seqNo"
21
- }
22
- });
23
- return [4 /*yield*/, dynamodb.send(putItemCommand)];
24
- case 1:
25
- _a.sent();
26
- return [2 /*return*/, msg];
5
+ const dynamodb = new DynamoDBClient();
6
+ export const putMessage = async (tableName, userId, message) => {
7
+ const msg = {
8
+ ...message,
9
+ seqNo: Date.now()
10
+ };
11
+ const messageWithKeys = { ...msg, userId };
12
+ const putItemCommand = new PutItemCommand({
13
+ TableName: tableName,
14
+ Item: marshall(messageWithKeys),
15
+ ConditionExpression: "attribute_not_exists(#userId) AND attribute_not_exists(#seqNo)",
16
+ ExpressionAttributeNames: {
17
+ "#userId": "userId",
18
+ "#seqNo": "seqNo"
27
19
  }
28
20
  });
29
- }); };
30
- export var validateIncomingMessage = function (message, userId) { return __awaiter(void 0, void 0, void 0, function () {
31
- var errorMessage, thread;
32
- return __generator(this, function (_a) {
33
- switch (_a.label) {
34
- case 0:
35
- errorMessage = undefined;
36
- return [4 /*yield*/, threadCache.get(message.threadId)];
37
- case 1:
38
- thread = _a.sent();
39
- if (thread === undefined) {
40
- errorMessage = "Invalid threadId : does not exist";
41
- }
42
- else if (!thread.participants.includes(userId)) {
43
- errorMessage = "Invalid threadId : from '".concat(userId, "' is not a participant in thread '").concat(thread.id, "'");
44
- }
45
- else if (!typeToAllowedActionsMap[message.type].includes(message.action)) {
46
- errorMessage = "Invalid action : action must be '".concat(typeToAllowedActionsMap[message.type].join(","), "' for '").concat(message.type, "' type");
47
- }
48
- if (errorMessage) {
49
- return [2 /*return*/, {
50
- statusCode: 400,
51
- headers: { "Content-Type": "application/json; charset=utf-8" },
52
- body: JSON.stringify({
53
- message: errorMessage,
54
- threadId: message.threadId
55
- })
56
- }];
57
- }
58
- return [2 /*return*/];
59
- }
60
- });
61
- }); };
21
+ await dynamodb.send(putItemCommand);
22
+ return msg;
23
+ };
24
+ export const validateIncomingMessage = async (message, userId) => {
25
+ let errorMessage = undefined;
26
+ const thread = await threadCache.get(message.threadId);
27
+ if (thread === undefined) {
28
+ errorMessage = "Invalid threadId : does not exist";
29
+ }
30
+ else if (!thread.participants.includes(userId)) {
31
+ errorMessage = `Invalid threadId : from '${userId}' is not a participant in thread '${thread.id}'`;
32
+ }
33
+ else if (!typeToAllowedActionsMap[message.type].includes(message.action)) {
34
+ errorMessage = `Invalid action : action must be '${typeToAllowedActionsMap[message.type].join(",")}' for '${message.type}' type`;
35
+ }
36
+ if (errorMessage) {
37
+ return {
38
+ statusCode: 400,
39
+ headers: { "Content-Type": "application/json; charset=utf-8" },
40
+ body: JSON.stringify({
41
+ message: errorMessage,
42
+ threadId: message.threadId
43
+ })
44
+ };
45
+ }
46
+ };
@@ -1,79 +1,68 @@
1
- var _a, _b;
2
- import { __awaiter, __generator } from "tslib";
3
1
  import { verify } from "jsonwebtoken";
4
2
  import { sessionRequirement } from "./types";
5
3
  import { threadCache } from "./threadCache";
6
- var sessionJwtSecret = (_a = process.env.SESSION_SECRET) !== null && _a !== void 0 ? _a : "";
7
- var sessionForce = (_b = process.env.SESSION_FORCE) !== null && _b !== void 0 ? _b : "";
8
- export var Error = {
4
+ const sessionJwtSecret = process.env.SESSION_SECRET ?? "";
5
+ const sessionForce = process.env.SESSION_FORCE ?? "";
6
+ export const Error = {
9
7
  required: "missing required field: sessionToken",
10
8
  invalid: "invalid field: sessionToken"
11
9
  };
12
- export var handleSessionToken = function (userId, threadId, type, action, sessionToken) { return __awaiter(void 0, void 0, void 0, function () {
13
- var result, isSessionRequiredForThisTypeAction, oneDay_1, thread_1, sessionRequired, session, now;
14
- var _a, _b, _c;
15
- return __generator(this, function (_d) {
16
- switch (_d.label) {
17
- case 0:
18
- result = { sessionId: "" };
19
- if (!sessionJwtSecret) return [3 /*break*/, 6];
20
- if (!!sessionToken) return [3 /*break*/, 5];
21
- isSessionRequiredForThisTypeAction = (_a = sessionRequirement[type]) === null || _a === void 0 ? void 0 : _a[action];
22
- if (!(isSessionRequiredForThisTypeAction !== undefined)) return [3 /*break*/, 4];
23
- oneDay_1 = Date.now() + 1000 * 60 * 60 * 24;
24
- if (!(isSessionRequiredForThisTypeAction === "always")) return [3 /*break*/, 1];
25
- result.error = Error.required;
26
- result.sessionRequiredTill = oneDay_1;
27
- return [3 /*break*/, 4];
28
- case 1:
29
- if (!(isSessionRequiredForThisTypeAction === "thread")) return [3 /*break*/, 4];
30
- if (!(sessionForce == "true")) return [3 /*break*/, 2];
31
- result.error = Error.required;
32
- result.sessionRequiredTill = oneDay_1;
33
- return [3 /*break*/, 4];
34
- case 2: return [4 /*yield*/, threadCache.get(threadId, -1)];
35
- case 3:
36
- thread_1 = _d.sent();
37
- sessionRequired = Object.fromEntries((_c = (_b = thread_1 === null || thread_1 === void 0 ? void 0 : thread_1.sessionRequired) === null || _b === void 0 ? void 0 : _b.map(function (userId, index) {
38
- var _a, _b;
39
- return [
40
- userId,
41
- (_b = (_a = thread_1 === null || thread_1 === void 0 ? void 0 : thread_1.sessionRequiredTill) === null || _a === void 0 ? void 0 : _a[index]) !== null && _b !== void 0 ? _b : oneDay_1
42
- ];
43
- })) !== null && _c !== void 0 ? _c : []);
44
- if (sessionRequired[userId] !== undefined &&
45
- Date.now() < sessionRequired[userId]) {
10
+ export const handleSessionToken = async (userId, threadId, type, action, sessionToken) => {
11
+ const result = { sessionId: "" };
12
+ if (sessionJwtSecret) {
13
+ if (!sessionToken) {
14
+ const isSessionRequiredForThisTypeAction = sessionRequirement[type]?.[action];
15
+ if (isSessionRequiredForThisTypeAction !== undefined) {
16
+ const oneDay = Date.now() + 1000 * 60 * 60 * 24; // 1 day
17
+ if (isSessionRequiredForThisTypeAction === "always") {
46
18
  result.error = Error.required;
47
- result.sessionRequiredTill = sessionRequired[userId];
19
+ result.sessionRequiredTill = oneDay;
48
20
  }
49
- _d.label = 4;
50
- case 4: return [3 /*break*/, 6];
51
- case 5:
52
- try {
53
- session = verify(sessionToken, sessionJwtSecret + "", {
54
- algorithms: ["HS512"],
55
- ignoreExpiration: true
56
- });
57
- now = Date.now();
58
- if (session.participants.includes(userId) &&
59
- session.startTime <= now &&
60
- now <= session.endTime) {
61
- result.sessionId = session.id;
21
+ else if (isSessionRequiredForThisTypeAction === "thread") {
22
+ if (sessionForce == "true") {
23
+ result.error = Error.required;
24
+ result.sessionRequiredTill = oneDay;
62
25
  }
63
26
  else {
64
- result.error = Error.invalid;
27
+ const thread = await threadCache.get(threadId, -1); // ttl = -1 will force the cache to fetch from db
28
+ const sessionRequired = Object.fromEntries(thread?.sessionRequired?.map((userId, index) => [
29
+ userId,
30
+ thread?.sessionRequiredTill?.[index] ?? oneDay
31
+ ]) ?? []);
32
+ if (sessionRequired[userId] !== undefined &&
33
+ Date.now() < sessionRequired[userId]) {
34
+ result.error = Error.required;
35
+ result.sessionRequiredTill = sessionRequired[userId];
36
+ }
65
37
  }
66
38
  }
67
- catch (err) {
68
- // eslint-disable-next-line no-console
69
- console.error("session token verification failed. userId=" +
70
- userId +
71
- ", error=" +
72
- err);
39
+ }
40
+ }
41
+ else {
42
+ try {
43
+ const session = verify(sessionToken, sessionJwtSecret + "", {
44
+ algorithms: ["HS512"],
45
+ ignoreExpiration: true
46
+ });
47
+ const now = Date.now();
48
+ if (session.participants.includes(userId) &&
49
+ session.startTime <= now &&
50
+ now <= session.endTime) {
51
+ result.sessionId = session.id;
52
+ }
53
+ else {
73
54
  result.error = Error.invalid;
74
55
  }
75
- _d.label = 6;
76
- case 6: return [2 /*return*/, result];
56
+ }
57
+ catch (err) {
58
+ // eslint-disable-next-line no-console
59
+ console.error("session token verification failed. userId=" +
60
+ userId +
61
+ ", error=" +
62
+ err);
63
+ result.error = Error.invalid;
64
+ }
77
65
  }
78
- });
79
- }); };
66
+ }
67
+ return result;
68
+ };
@@ -1,31 +1,18 @@
1
- import { __awaiter, __generator } from "tslib";
2
1
  import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";
3
2
  import { cache } from "./cache";
4
3
  import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
5
- var dynamodb = new DynamoDBClient();
6
- var _threadCache = cache(100, 60 * 60000);
7
- export var threadCache = {
8
- get: function (threadId, ttl) { return __awaiter(void 0, void 0, void 0, function () {
9
- return __generator(this, function (_a) {
10
- switch (_a.label) {
11
- case 0: return [4 /*yield*/, _threadCache.get(threadId, function () { return __awaiter(void 0, void 0, void 0, function () {
12
- var threadResult;
13
- return __generator(this, function (_a) {
14
- switch (_a.label) {
15
- case 0: return [4 /*yield*/, dynamodb.send(new GetItemCommand({
16
- TableName: process.env.THREAD_TABLE_NAME,
17
- Key: marshall({ id: threadId })
18
- }))];
19
- case 1:
20
- threadResult = _a.sent();
21
- return [2 /*return*/, threadResult.Item
22
- ? unmarshall(threadResult.Item)
23
- : undefined];
24
- }
25
- });
26
- }); }, ttl)];
27
- case 1: return [2 /*return*/, _a.sent()];
28
- }
29
- });
30
- }); }
4
+ const dynamodb = new DynamoDBClient();
5
+ const _threadCache = cache(100, 60 * 60000);
6
+ export const threadCache = {
7
+ get: async (threadId, ttl) => {
8
+ return await _threadCache.get(threadId, async () => {
9
+ const threadResult = await dynamodb.send(new GetItemCommand({
10
+ TableName: process.env.THREAD_TABLE_NAME,
11
+ Key: marshall({ id: threadId })
12
+ }));
13
+ return threadResult.Item
14
+ ? unmarshall(threadResult.Item)
15
+ : undefined;
16
+ }, ttl);
17
+ }
31
18
  };
@@ -1,4 +1,4 @@
1
- export var typeToAllowedActionsMap = {
1
+ export const typeToAllowedActionsMap = {
2
2
  text: ["new", "edit"],
3
3
  image: ["new", "edit"],
4
4
  control: [
@@ -15,7 +15,7 @@ export var typeToAllowedActionsMap = {
15
15
  /**
16
16
  * Store the message type and actions for which the session token is required
17
17
  */
18
- export var sessionRequirement = {
18
+ export const sessionRequirement = {
19
19
  text: { new: "thread", edit: "thread" },
20
20
  image: { new: "thread", edit: "thread" },
21
21
  control: {
@@ -1,4 +1,3 @@
1
- import { __assign, __awaiter, __generator, __rest } from "tslib";
2
1
  import { RouteBuilder } from "somod-http-extension";
3
2
  import { UserProviderMiddlewareKey } from "../../lib";
4
3
  import { putMessage, validateIncomingMessage } from "../../lib/message";
@@ -7,97 +6,85 @@ import { QueryCommand, DynamoDBClient } from "@aws-sdk/client-dynamodb";
7
6
  import { convertToAttr, unmarshall } from "@aws-sdk/util-dynamodb";
8
7
  import { handleSessionToken } from "../../lib/sessionUtil";
9
8
  import { getUserIdFromEvent } from "../../lib/getUserIdFromEvent";
10
- var dynamodb = new DynamoDBClient();
11
- var builder = new RouteBuilder();
12
- var postMessageHandler = function (request, event) { return __awaiter(void 0, void 0, void 0, function () {
13
- var userId, messageValidationError, sessionIdResult, _a, sessionToken, msg, actions, message;
14
- return __generator(this, function (_b) {
15
- switch (_b.label) {
16
- case 0:
17
- userId = event.somodMiddlewareContext.get(UserProviderMiddlewareKey);
18
- return [4 /*yield*/, validateIncomingMessage(request.body, userId)];
19
- case 1:
20
- messageValidationError = _b.sent();
21
- if (messageValidationError) {
22
- return [2 /*return*/, messageValidationError];
23
- }
24
- return [4 /*yield*/, handleSessionToken(userId, request.body.threadId, request.body.type, request.body.action, request.body.sessionToken)];
25
- case 2:
26
- sessionIdResult = _b.sent();
27
- if (sessionIdResult.error) {
28
- return [2 /*return*/, {
29
- statusCode: 400,
30
- headers: { "Content-Type": "application/json; charset=utf-8" },
31
- body: JSON.stringify({
32
- message: sessionIdResult.error,
33
- threadId: request.body.threadId,
34
- sessionRequiredTill: sessionIdResult.sessionRequiredTill
35
- })
36
- }];
37
- }
38
- _a = request.body, sessionToken = _a.sessionToken, msg = __rest(_a, ["sessionToken"]);
39
- actions = ["sessionStart", "sessionExtend", "sessionEnd"];
40
- return [4 /*yield*/, putMessage(process.env.MESSAGE_BOX_TABLE_NAME + "", userId, __assign(__assign(__assign({}, msg), { sessionId: sessionIdResult.sessionId, id: v1uuid().split("-").join(""), from: userId, sentAt: Date.now() }), (actions.includes(request.body.action)
41
- ? { sessionToken: sessionToken }
42
- : {})))];
43
- case 3:
44
- message = _b.sent();
45
- return [2 /*return*/, {
46
- statusCode: 200,
47
- headers: { "Content-Type": "application/json; charset=utf-8" },
48
- body: JSON.stringify({
49
- id: message.id,
50
- seqNo: message.seqNo,
51
- sentAt: message.sentAt,
52
- from: message.from
53
- })
54
- }];
55
- }
9
+ const dynamodb = new DynamoDBClient();
10
+ const builder = new RouteBuilder();
11
+ const postMessageHandler = async (request, event) => {
12
+ const userId = event.somodMiddlewareContext.get(UserProviderMiddlewareKey);
13
+ const messageValidationError = await validateIncomingMessage(request.body, userId);
14
+ if (messageValidationError) {
15
+ return messageValidationError;
16
+ }
17
+ const sessionIdResult = await handleSessionToken(userId, request.body.threadId, request.body.type, request.body.action, request.body.sessionToken);
18
+ if (sessionIdResult.error) {
19
+ return {
20
+ statusCode: 400,
21
+ headers: { "Content-Type": "application/json; charset=utf-8" },
22
+ body: JSON.stringify({
23
+ message: sessionIdResult.error,
24
+ threadId: request.body.threadId,
25
+ sessionRequiredTill: sessionIdResult.sessionRequiredTill
26
+ })
27
+ };
28
+ }
29
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30
+ const { sessionToken, ...msg } = request.body;
31
+ const actions = ["sessionStart", "sessionExtend", "sessionEnd"];
32
+ const message = await putMessage(process.env.MESSAGE_BOX_TABLE_NAME + "", userId, {
33
+ ...msg,
34
+ sessionId: sessionIdResult.sessionId,
35
+ id: v1uuid().split("-").join(""),
36
+ from: userId,
37
+ sentAt: Date.now(),
38
+ ...(actions.includes(request.body.action)
39
+ ? { sessionToken: sessionToken }
40
+ : {})
56
41
  });
57
- }); };
58
- var syncMessagesHandler = function (request, event) { return __awaiter(void 0, void 0, void 0, function () {
59
- var userId, queryCommandInput, queryCommand, result, messages;
60
- return __generator(this, function (_a) {
61
- switch (_a.label) {
62
- case 0:
63
- userId = getUserIdFromEvent(event);
64
- queryCommandInput = {
65
- TableName: process.env.MESSAGE_BOX_TABLE_NAME + "",
66
- KeyConditionExpression: "#userId = :userId",
67
- ExpressionAttributeNames: {
68
- "#userId": "userId"
69
- },
70
- ExpressionAttributeValues: {
71
- ":userId": convertToAttr(userId)
72
- },
73
- Limit: 100
74
- };
75
- if (request.parameters.query.from) {
76
- queryCommandInput.KeyConditionExpression += " AND #seqNo >= :seqNo";
77
- queryCommandInput.ExpressionAttributeNames["#seqNo"] = "seqNo";
78
- queryCommandInput.ExpressionAttributeValues[":seqNo"] = {
79
- N: request.parameters.query.from
80
- };
81
- }
82
- queryCommand = new QueryCommand(queryCommandInput);
83
- return [4 /*yield*/, dynamodb.send(queryCommand)];
84
- case 1:
85
- result = _a.sent();
86
- messages = (result.Items || []).map(function (item) {
87
- var message = unmarshall(item);
88
- delete message.userId;
89
- return message;
90
- });
91
- return [2 /*return*/, {
92
- statusCode: 200,
93
- headers: {
94
- "Content-Type": "application/json; charset=utf-8"
95
- },
96
- body: JSON.stringify(messages)
97
- }];
98
- }
42
+ return {
43
+ statusCode: 200,
44
+ headers: { "Content-Type": "application/json; charset=utf-8" },
45
+ body: JSON.stringify({
46
+ id: message.id,
47
+ seqNo: message.seqNo,
48
+ sentAt: message.sentAt,
49
+ from: message.from
50
+ })
51
+ };
52
+ };
53
+ const syncMessagesHandler = async (request, event) => {
54
+ const userId = getUserIdFromEvent(event);
55
+ const queryCommandInput = {
56
+ TableName: process.env.MESSAGE_BOX_TABLE_NAME + "",
57
+ KeyConditionExpression: "#userId = :userId",
58
+ ExpressionAttributeNames: {
59
+ "#userId": "userId"
60
+ },
61
+ ExpressionAttributeValues: {
62
+ ":userId": convertToAttr(userId)
63
+ },
64
+ Limit: 100
65
+ };
66
+ if (request.parameters.query.from) {
67
+ queryCommandInput.KeyConditionExpression += " AND #seqNo >= :seqNo";
68
+ queryCommandInput.ExpressionAttributeNames["#seqNo"] = "seqNo";
69
+ queryCommandInput.ExpressionAttributeValues[":seqNo"] = {
70
+ N: request.parameters.query.from
71
+ };
72
+ }
73
+ const queryCommand = new QueryCommand(queryCommandInput);
74
+ const result = await dynamodb.send(queryCommand);
75
+ const messages = (result.Items || []).map(item => {
76
+ const message = unmarshall(item);
77
+ delete message.userId;
78
+ return message;
99
79
  });
100
- }); };
80
+ return {
81
+ statusCode: 200,
82
+ headers: {
83
+ "Content-Type": "application/json; charset=utf-8"
84
+ },
85
+ body: JSON.stringify(messages)
86
+ };
87
+ };
101
88
  builder.add("/post-message", "POST", postMessageHandler);
102
89
  builder.add("/sync-messages", "GET", syncMessagesHandler);
103
90
  export default builder.getHandler();