From 1c961cb04afcc8b195a5f3a9fa19c5b700093ece Mon Sep 17 00:00:00 2001 From: Oleksandr <98953475+Oleksandr1414@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:16:31 +0200 Subject: [PATCH] Release 0.27.0 (#143) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SK-217 Implemented group chat images (#117) * SK-189 added `avatar_object` field for user object * SK-189 implemented the logic of creating links for downloading user avatars * SK-189 renamed downloadFile to getFileDownloadUrl * SK-217 added `image_object` for Conversation fields * SK-217 added `image_url` field to the system event * SK-217 added sending update request to other users * SK-217 cleanup * SK-217 minor fix * SK-217 added image_object for conversationCreate * SK-217 add log * SK-217 remove log * SK-217 add log * SK-217 remove log * SK-194 updated `typing` object (#121) * Sk-194 updated typing object * SK-194 updated API.md * SK-234 removed file name from uuid in file `object_id` (#122) * updated CHANGELOG.md (0.24.0 -> 0.25.0) * Updated tests for status typing (removed tests for deleted fields) * FM-27 added device_token for push subscription (#136) * Release 0.25.0 (#123) * SK-217 Implemented group chat images (#117) * SK-189 added `avatar_object` field for user object * SK-189 implemented the logic of creating links for downloading user avatars * SK-189 renamed downloadFile to getFileDownloadUrl * SK-217 added `image_object` for Conversation fields * SK-217 added `image_url` field to the system event * SK-217 added sending update request to other users * SK-217 cleanup * SK-217 minor fix * SK-217 added image_object for conversationCreate * SK-217 add log * SK-217 remove log * SK-217 add log * SK-217 remove log * SK-194 updated `typing` object (#121) * Sk-194 updated typing object * SK-194 updated API.md * SK-234 removed file name from uuid in file `object_id` (#122) * updated CHANGELOG.md (0.24.0 -> 0.25.0) * Release 0.25.0 (#124) * SK-217 Implemented group chat images (#117) * SK-189 added `avatar_object` field for user object * SK-189 implemented the logic of creating links for downloading user avatars * SK-189 renamed downloadFile to getFileDownloadUrl * SK-217 added `image_object` for Conversation fields * SK-217 added `image_url` field to the system event * SK-217 added sending update request to other users * SK-217 cleanup * SK-217 minor fix * SK-217 added image_object for conversationCreate * SK-217 add log * SK-217 remove log * SK-217 add log * SK-217 remove log * SK-194 updated `typing` object (#121) * Sk-194 updated typing object * SK-194 updated API.md * SK-234 removed file name from uuid in file `object_id` (#122) * updated CHANGELOG.md (0.24.0 -> 0.25.0) * Updated tests for status typing (removed tests for deleted fields) * Update API.md * Update README.md * Update README.md * Update README.md * Update README.md * updated package.json * Update README.md * Сhange development flow (#132) * FM-27 added `device_token` for push subscription * FM-27 minor cleanup * FM-27 removed `rsmq-promise`, updated bull queue methods * FM-27 renamed minor comments --------- Co-authored-by: IgorKhomenko <70977170+IgorKhomenko@users.noreply.github.com> Co-authored-by: Igor Khomenko Co-authored-by: ku9nov <69673517+ku9nov@users.noreply.github.com> * updated CHANGELOG.md * renamed env file * updated tests for `push_subcriptions`, added indexes for the `push_subcriptions` collection * SK-273 remove `platform` field from pushEvent object (#141) * SK-272 extended `conversation_list` request to allow request convs by ids (#142) * SK-272 added `ids` options for conversation_list, added test, updated API.md * SK-272 added limit for ids field - 10 * SK-272 updated API.md * Updated CHANGELOG.md * Fixes different naming of methods --------- Co-authored-by: IgorKhomenko <70977170+IgorKhomenko@users.noreply.github.com> Co-authored-by: Igor Khomenko Co-authored-by: ku9nov <69673517+ku9nov@users.noreply.github.com> --- APIs/JSON/controllers/push_notifications.js | 6 +-- .../conversations_schema_validation.js | 1 + CHANGELOG.md | 11 +++++ app/lib/push_queue/base.js | 14 +++--- app/lib/push_queue/sama_native_queue.js | 31 ++++++------- .../operations/conversation/list/index.js | 4 +- .../conversation_participants/index.js | 6 +++ app/providers/services/conversation/index.js | 13 +++++- .../push_notifications_repository.js | 40 +++-------------- docs/API.md | 4 ++ package-lock.json | 4 +- package.json | 2 +- test/conversations.js | 44 +++++++++++++++++++ test/push_notifications.js | 4 +- 14 files changed, 115 insertions(+), 69 deletions(-) diff --git a/APIs/JSON/controllers/push_notifications.js b/APIs/JSON/controllers/push_notifications.js index 86f92309..0037acbb 100644 --- a/APIs/JSON/controllers/push_notifications.js +++ b/APIs/JSON/controllers/push_notifications.js @@ -104,11 +104,9 @@ class PushNotificationsController extends BaseJSONController { user_ids: recipients_ids, }) - const pushEvents = await RuntimeDefinedContext.PUSH_QUEUE_DRIVER.createPushEvents(createPushEventOptions) + const pushEvent = await RuntimeDefinedContext.PUSH_QUEUE_DRIVER.createPushEvent(createPushEventOptions) - const responsePushEvents = pushEvents.map((pushEvent) => pushEvent.visibleParams()) - - return new Response().addBackMessage({ response: { id: requestId, event: responsePushEvents } }) + return new Response().addBackMessage({ response: { id: requestId, event: pushEvent.visibleParams() } }) } } diff --git a/APIs/JSON/validations/conversations_schema_validation.js b/APIs/JSON/validations/conversations_schema_validation.js index 5beb6310..a884fbee 100644 --- a/APIs/JSON/validations/conversations_schema_validation.js +++ b/APIs/JSON/validations/conversations_schema_validation.js @@ -83,6 +83,7 @@ export const conversationsSchemaValidation = { updated_at: Joi.object({ gt: Joi.date(), }), + ids: Joi.array().items(Joi.alternatives().try(Joi.object(), Joi.string())).max(10), }).required(), delete: Joi.object({ id: Joi.string().required(), diff --git a/CHANGELOG.md b/CHANGELOG.md index d538c2d5..93947e35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.27.0 + +### Imporvments + +- Extended the conversation_list request to allow requesting conversations by ids + +### Updated + +- Updated indexes of the `push_subscriptions` collection +- Removed the `platform` field from the `pushEvent` object + ## 0.26.0 ### Features diff --git a/app/lib/push_queue/base.js b/app/lib/push_queue/base.js index 78a33424..a8eafd80 100644 --- a/app/lib/push_queue/base.js +++ b/app/lib/push_queue/base.js @@ -4,26 +4,26 @@ import CreateChatAlertEventOptions from "./models/CreateChatAlertEventOptions.js import CreatePushEventOptions from "./models/CreatePushEventOptions.js" export default class BasePushQueue { - async buildPushEvents(createPushEventOptions) { - const pushEvents = await pushNotificationsRepository.createPushEvents( + async buildPushEvent(createPushEventOptions) { + const pushEvent = await pushNotificationsRepository.createPushEvent( createPushEventOptions.user_id, createPushEventOptions.user_ids, createPushEventOptions.payload, {} ) - return pushEvents + return pushEvent } - async getSubscriptionsByPlatform(platform, user_id) { - return await pushNotificationsRepository.getSubscriptionsByPlatform(platform, user_id) + async getSubscriptionsByUid(user_id) { + return await pushNotificationsRepository.getSubscriptionsByUid(user_id) } async createPush(pushQueueMessage) { if (pushQueueMessage instanceof CreateChatAlertEventOptions) { return await this.createChatAlert(pushQueueMessage) } else if (pushQueueMessage instanceof CreatePushEventOptions) { - return await this.createPushEvents(pushQueueMessage) + return await this.createPushEvent(pushQueueMessage) } throw new Error("Unknown push message type") @@ -33,7 +33,7 @@ export default class BasePushQueue { throw new Error("Not implemented") } - async createPushEvents() { + async createPushEvent() { throw new Error("Not implemented") } } diff --git a/app/lib/push_queue/sama_native_queue.js b/app/lib/push_queue/sama_native_queue.js index 3b92cdb1..f6389b8f 100644 --- a/app/lib/push_queue/sama_native_queue.js +++ b/app/lib/push_queue/sama_native_queue.js @@ -17,34 +17,31 @@ export default class SamaNativePushQueue extends BasePushQueue { { user_ids: createChatAlertEventOptions.offlineUsersIDs } ) - const pushEvents = await this.buildPushEvents(createPushEventOptions) + const pushEvent = await this.buildPushEvent(createPushEventOptions) - await this.#addToQueue(pushEvents) + await this.#addToQueue(pushEvent) } - async createPushEvents(createPushEventOptions) { - const pushEvents = await this.buildPushEvents(createPushEventOptions) + async createPushEvent(createPushEventOptions) { + const pushEvents = await this.buildPushEvent(createPushEventOptions) await this.#addToQueue(pushEvents) return pushEvents } - async #addToQueue(pushEvents) { - for (const pushEvent of pushEvents) { - let devices = [] - const platform = pushEvent.params.platform + async #addToQueue(pushEvent) { + let devices = [] - for (const uid of pushEvent.params.user_ids) { - const userDevices = await this.getSubscriptionsByPlatform(platform, uid) - if (!userDevices.length) continue - devices = devices.concat(userDevices) - } + for (const uid of pushEvent.params.user_ids) { + const userDevices = await this.getSubscriptionsByUid(uid) + if (!userDevices.length) continue + devices = devices.concat(userDevices) + } - if (!Object.keys(devices).length) continue + if (!Object.keys(devices).length) return - const data = { devices, message: pushEvent.params.message, platform } - await this.queue.add(data) - } + const data = { devices, message: pushEvent.params.message } + await this.queue.add(data) } } diff --git a/app/providers/operations/conversation/list/index.js b/app/providers/operations/conversation/list/index.js index e7f126c6..c03ab100 100644 --- a/app/providers/operations/conversation/list/index.js +++ b/app/providers/operations/conversation/list/index.js @@ -10,7 +10,7 @@ class ConversationListOperation { } async perform(ws, options) { - const { limit, updated_at } = options + const { limit, updated_at, ids } = options const normalizedLimit = this.#normalizeLimitParam(limit) const currentUserId = this.sessionService.getSessionUserId(ws) @@ -18,7 +18,7 @@ class ConversationListOperation { const conversations = await this.conversationService.conversationsList( currentUser, - { updatedAt: updated_at }, + { updatedAt: updated_at, ids }, normalizedLimit ) diff --git a/app/providers/repositories/conversation_participants/index.js b/app/providers/repositories/conversation_participants/index.js index c0272847..b6713329 100644 --- a/app/providers/repositories/conversation_participants/index.js +++ b/app/providers/repositories/conversation_participants/index.js @@ -26,6 +26,12 @@ class ConversationParticipantRepository extends BaseRepository { return conversationsParticipants } + async findUserConversationIds(conversationIds, user_id) { + const availableConversationParticipants = await this.findAll({ conversation_id: { $in: conversationIds }, user_id }) + + return availableConversationParticipants.map((participant) => participant.conversation_id) + } + async findParticipantConversations(userId, limit) { const conversationParticipants = await this.findAll({ user_id: userId }, null, limit) diff --git a/app/providers/services/conversation/index.js b/app/providers/services/conversation/index.js index 2a2ebb8c..07c80598 100644 --- a/app/providers/services/conversation/index.js +++ b/app/providers/services/conversation/index.js @@ -41,7 +41,9 @@ class ConversationService { } async conversationsList(user, options, limit) { - const conversationIds = await this.conversationParticipantRepo.findParticipantConversations(user.native_id, limit) + const conversationIds = await (options.ids?.length + ? this.validateConvIdsWhichUserHasAccess(options.ids, user.native_id) + : this.conversationParticipantRepo.findParticipantConversations(user.native_id, limit)) const filterOptions = {} if (options.updatedAt?.gt) { @@ -82,6 +84,15 @@ class ConversationService { return conversationsParticipants.map((participant) => participant.user_id) } + async validateConvIdsWhichUserHasAccess(conversationIds, userId) { + const verifiedConversationIds = await this.conversationParticipantRepo.findUserConversationIds( + conversationIds, + userId + ) + + return verifiedConversationIds + } + async hasAccessToConversation(conversationId, userId) { const result = { conversation: null, asParticipant: false, asOwner: false, participantIds: null } diff --git a/app/repositories/push_notifications_repository.js b/app/repositories/push_notifications_repository.js index ac6a82cb..ef421af9 100644 --- a/app/repositories/push_notifications_repository.js +++ b/app/repositories/push_notifications_repository.js @@ -10,24 +10,9 @@ class PushNotificationsRepository extends BaseRepository { this.PushSubscriptionModel = PushSubscriptionModel } - async #usersPlatforms(users_ids) { - let notificationChannelIds = new Set() - - for (const user_id of users_ids) { - let userSubscriptions = await this.PushSubscriptionModel.findAll({ user_id: user_id }) - - userSubscriptions = userSubscriptions ?? [] - - for (const subscription of userSubscriptions) { - notificationChannelIds.add(subscription.platform) - } - } - - return [...notificationChannelIds] - } - - async getSubscriptionsByPlatform(platform, user_id) { - return await this.PushSubscriptionModel.findAll({ user_id, platform }, [ + async getSubscriptionsByUid(user_id) { + return await this.PushSubscriptionModel.findAll({ user_id }, [ + "platform", "web_endpoint", "web_key_auth", "web_key_p256dh", @@ -35,30 +20,19 @@ class PushNotificationsRepository extends BaseRepository { ]) } - async createPushEvents(userId, userIds, payload, options) { - const platforms = await this.#usersPlatforms(userIds) - + async createPushEvent(userId, userIds, payload, options) { const base64Payload = Buffer.from(JSON.stringify(payload)).toString("base64") - const pushEvents = [] - const pushEventParams = { user_id: userId, user_ids: userIds, message: base64Payload, } + const pushEvent = new this.Model(pushEventParams) + await pushEvent.save() - for (const platform of platforms) { - pushEventParams.platform = platform - - const pushEvent = new this.Model(pushEventParams) - await pushEvent.save() - - pushEvents.push(pushEvent) - } - - return pushEvents + return pushEvent } } diff --git a/docs/API.md b/docs/API.md index 2af94274..88c3a469 100644 --- a/docs/API.md +++ b/docs/API.md @@ -333,6 +333,10 @@ Later, the subsequent logins can be done via `token`: }, }; +You can also add the `ids` parameter if you need to get the target chat objects. The maximum number of ids in a request is 10. +For example: ["63480e68f4794709f802a2fa", "63480e68f4794709f802a2fy"] + + { response: { id: "54", diff --git a/package-lock.json b/package-lock.json index 61d066e7..4be2e84d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sama", - "version": "0.26.0", + "version": "0.27.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sama", - "version": "0.26.0", + "version": "0.27.0", "license": "GPL-3.0", "dependencies": { "@aws-sdk/client-s3": "^3.327.0", diff --git a/package.json b/package.json index 1117bb83..a38b8973 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sama", - "version": "0.26.0", + "version": "0.27.0", "description": "Simple but Advanced Messaging Alternative", "main": "index.js", "type": "module", diff --git a/test/conversations.js b/test/conversations.js index e17441bb..c60a9bb6 100644 --- a/test/conversations.js +++ b/test/conversations.js @@ -653,6 +653,50 @@ describe("Conversation functions", async () => { assert.equal(responseData.response.error, undefined) }) + it("should work with ids", async () => { + const requestData = { + request: { + conversation_list: { + ids: ArrayOfTmpConversaionts, + }, + id: "3_1", + }, + } + + let responseData = await packetJsonProcessor.processMessageOrError("test", JSON.stringify(requestData)) + + responseData = responseData.backMessages.at(0) + + const conversations = responseData.response.conversations + + assert.strictEqual(requestData.request.id, responseData.response.id) + assert.notEqual(responseData.response.conversations, undefined) + assert.equal( + conversations.some((el) => !ArrayOfTmpConversaionts.includes(el._id.toString())), + false + ) + assert.equal(responseData.response.error, undefined) + }) + + it("should fail max ids 10", async () => { + const requestData = { + request: { + conversation_list: { + ids: ["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"], + }, + id: "3_1", + }, + } + + let responseData = await packetJsonProcessor.processMessageOrError("test", JSON.stringify(requestData)) + + responseData = responseData.backMessages.at(0) + + assert.strictEqual(requestData.request.id, responseData.response.id) + assert.equal(responseData.response.conversations, undefined) + assert.equal(responseData.response.error, '"ids" must contain less than or equal to 10 items') + }) + it("should fail limit exceeded", async () => { await sendLogout("test", currentUserToken) currentUserToken = (await sendLogin("test", "user_1")).response.user.token diff --git a/test/push_notifications.js b/test/push_notifications.js index b32462c0..c1255515 100644 --- a/test/push_notifications.js +++ b/test/push_notifications.js @@ -464,7 +464,7 @@ describe("PushNotification functions", async () => { responseData = responseData.backMessages.at(0) - const event = responseData.response.event.at(0) + const event = responseData.response.event const eventMessage = Buffer.from(event.message, "base64").toString("utf8") @@ -542,7 +542,7 @@ describe("PushNotification functions", async () => { responseData = responseData.backMessages.at(0) - const event = responseData.response.event.at(0) + const event = responseData.response.event assert.strictEqual(requestData.request.id, responseData.response.id) assert.strictEqual(event.user_ids[0], usersIds[0].toString())