From 43f4a4b0d578a6664858c74490c2ecbc83f0bae4 Mon Sep 17 00:00:00 2001 From: Alrik Zachert Date: Mon, 27 Mar 2023 19:51:45 +0200 Subject: [PATCH 1/5] Add support for deleting users via admin api. --- CHANGELOG.md | 1 + .../v1/routes/realms/{realmId}/users/{id}.js | 22 +++++++ .../routes/realms/{realmId}/users/{id}.yaml | 25 ++++++++ src/tasks/admin/delete-user.js | 53 ++++++++++++++++ src/tasks/admin/delete-user.spec.js | 62 +++++++++++++++++++ src/tasks/admin/index.js | 8 +++ 6 files changed, 171 insertions(+) create mode 100644 src/api/admin/v1/routes/realms/{realmId}/users/{id}.js create mode 100644 src/api/admin/v1/routes/realms/{realmId}/users/{id}.yaml create mode 100644 src/tasks/admin/delete-user.js create mode 100644 src/tasks/admin/delete-user.spec.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 6191fa4..148fc9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Introduce new administrative channel management endpoints `/admin/v1/realms/{realmId}/channels`. - Introduce new administrative subscription management endpoints `/admin/v1/realms/{realmId}/subscriptions`. - Introduce new administrative endpoint for deleting auth tokens `DELETE /admin/v1/realms/{realmId}/tokens/{tokenId}` +- Introduce new administrative endpoint for deleting users `DELETE /admin/v1/realms/{realmId}/users/{userId}` ### Changed - Add support for upgraded verneMQ broker. This requires the acceptance of their [EULA](https://vernemq.com/end-user-license-agreement/), diff --git a/src/api/admin/v1/routes/realms/{realmId}/users/{id}.js b/src/api/admin/v1/routes/realms/{realmId}/users/{id}.js new file mode 100644 index 0000000..da81643 --- /dev/null +++ b/src/api/admin/v1/routes/realms/{realmId}/users/{id}.js @@ -0,0 +1,22 @@ +/** + * @param {{admin: AdminTasks}} tasks Tasks + * @returns {{delete: delete}} Method handlers + */ +module.exports = tasks => ({ + /** + * DELETE /realms/{realmId}/users/{id} + * @param {object} request Request + * @param {object} response Response + */ + async delete(request, response) { + const {realmId, id} = request.params; + + const {ok, error} = await tasks.admin.deleteUser({realmId, id}); + + if (!ok) { + throw error; + } + + response.status(204).send(); + }, +}); diff --git a/src/api/admin/v1/routes/realms/{realmId}/users/{id}.yaml b/src/api/admin/v1/routes/realms/{realmId}/users/{id}.yaml new file mode 100644 index 0000000..5dd63e4 --- /dev/null +++ b/src/api/admin/v1/routes/realms/{realmId}/users/{id}.yaml @@ -0,0 +1,25 @@ +/realms/{realmId}/users/{id}: + parameters: + - $ref: '#/parameters/realmIdInPath' + - name: id + description: The user id. + in: path + required: true + type: string + pattern: ^[\w-]+$ + + delete: + summary: Delete the user referenced by id. + description: | + Delete the user and all sub resources like subscriptions, auth tokens and realtime connections. + tags: + - Users + responses: + 204: + description: User has been deleted successfully. + 400: + description: Request validation failed. + 401: + description: The user could not be deleted due to failed authorization. + 404: + description: The requested user does not exist. diff --git a/src/tasks/admin/delete-user.js b/src/tasks/admin/delete-user.js new file mode 100644 index 0000000..5d32da0 --- /dev/null +++ b/src/tasks/admin/delete-user.js @@ -0,0 +1,53 @@ +const {success, failure} = require('../../lib/result'); +const taskError = require('../../lib/error/task'); +const createTaskError = require('../../lib/error/task'); + +/** + * Init delete user task. + * @param {RealmRepository} realmRepository Realm repository + * @param {UserRepository} userRepository The user repository + * @param {AuthRepository} authRepository The auth repository + * @param {RealtimeConnectionRepository} realtimeConnectionRepository The realtime connection repository + * @param {SubscriptionRepository} subscriptionRepository The subscription repository + * @returns {ClientTasks#deleteUser} Task + */ +module.exports = ({ + realmRepository, + userRepository, + authRepository, + subscriptionRepository, + realtimeConnectionRepository, +}) => + /** + * Delete a user and its auth tokens and subscriptions. + * @function AdminTasks#deleteUser + * @param {string} realmId Realm id + * @param {string} id User id + * @returns {Result} + */ + async ({realmId, id}) => { + const realm = await realmRepository.findOne({id: realmId}); + if (!realm) { + return failure(createTaskError({ + code: 'UnknownRealm', + message: 'Cannot lookup the given realm.', + })); + } + + const user = await userRepository.findOne({realmId, id}); + if (!user) { + return failure(taskError({ + code: 'UnknownUser', + message: 'User does not exists.', + })); + } + + await Promise.all([ + userRepository.deleteOne({realmId, id}), + authRepository.deleteAllByUserId({realmId, userId: id}), + subscriptionRepository.deleteAllByUserId({realmId, userId: id}), + realtimeConnectionRepository.deleteAllByUserId({realmId, userId: id}), + ]); + + return success(user); + }; diff --git a/src/tasks/admin/delete-user.spec.js b/src/tasks/admin/delete-user.spec.js new file mode 100644 index 0000000..ec7ea5e --- /dev/null +++ b/src/tasks/admin/delete-user.spec.js @@ -0,0 +1,62 @@ +const userRepository = require('../../lib/test/mocks/repositories/user'); +const realmRepository = require('../../lib/test/mocks/repositories/realm'); +const authRepository = require('../../lib/test/mocks/repositories/auth'); +const realtimeConnectionRepository = require('../../lib/test/mocks/repositories/realtime-connection'); +const subscriptionRepository = require('../../lib/test/mocks/repositories/subscription'); +const initDeleteUser = require('./delete-user'); + +describe('The admin deleteUser task', () => { + let deleteUser; + + beforeEach(() => { + userRepository.deleteOne = jest.fn(); + authRepository.deleteAllByUserId = jest.fn(); + subscriptionRepository.deleteAllByUserId = jest.fn(); + realtimeConnectionRepository.deleteAllByUserId = jest.fn(); + deleteUser = initDeleteUser({ + realmRepository, + userRepository, + authRepository, + subscriptionRepository, + realtimeConnectionRepository, + }); + }); + + describe('when called with an invalid realm id', () => { + it('should fail with appropriate error', async () => { + const {ok, error} = await deleteUser({realmId: realmRepository.unknownRealmId, id: userRepository.knownUserId}); + + expect(ok).toBe(false); + expect(error).toBeDefined(); + expect(error.code).toBe('UnknownRealm'); + }); + }); + + describe('when given an unknown user id', () => { + it('should fail with appropriate error', async () => { + const {ok, error} = await deleteUser({ + realmId: realmRepository.knownRealmId, + id: userRepository.unknownUserId, + }); + + expect(ok).toBe(false); + expect(error).toBeDefined(); + expect(error.code).toBe('UnknownUser'); + }); + }); + + describe('when configured correctly', () => { + it('should delete the user', async () => { + const {ok} = await deleteUser({ + realmId: realmRepository.knownRealmId, + id: userRepository.knownUserId, + }); + + expect(ok).toBe(true); + expect(userRepository.deleteOne).toHaveBeenCalled(); + expect(authRepository.deleteAllByUserId).toHaveBeenCalled(); + expect(subscriptionRepository.deleteAllByUserId).toHaveBeenCalled(); + expect(realtimeConnectionRepository.deleteAllByUserId).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/tasks/admin/index.js b/src/tasks/admin/index.js index d38089b..d7f9660 100644 --- a/src/tasks/admin/index.js +++ b/src/tasks/admin/index.js @@ -4,6 +4,7 @@ const initCreateRealmToken = require('./create-realm-token'); const initCreateSubscription = require('./create-subscription'); const initCreateUser = require('./create-user'); const initDeleteToken = require('./delete-token'); +const initDeleteUser = require('./delete-user'); const initFetchRealm = require('./fetch-realm'); const initListChannels = require('./list-channels'); const initListRealms = require('./list-realms'); @@ -50,6 +51,13 @@ module.exports = ({ realmRepository, realtimeConnectionRepository, }), + deleteUser: initDeleteUser({ + authRepository, + realmRepository, + realtimeConnectionRepository, + userRepository, + subscriptionRepository, + }), listChannels: initListChannels({realmRepository, channelRepository}), listRealms: initListRealms({realmRepository}), listRealmTokens: initListRealmTokens({realmRepository, authRepository}), From 5fb90da1daf823a14568b2e9235035fe12c941ef Mon Sep 17 00:00:00 2001 From: Alrik Zachert Date: Mon, 27 Mar 2023 21:59:59 +0200 Subject: [PATCH 2/5] Add support for deleting channels via admin api. --- .../routes/realms/{realmId}/channels/{id}.js | 22 +++++ .../realms/{realmId}/channels/{id}.yaml | 25 ++++++ src/bootstrap/tasks.js | 1 + src/repositories/message.js | 14 ++++ src/tasks/admin/delete-channel.js | 49 +++++++++++ src/tasks/admin/delete-channel.spec.js | 84 +++++++++++++++++++ src/tasks/admin/index.js | 9 ++ 7 files changed, 204 insertions(+) create mode 100644 src/api/admin/v1/routes/realms/{realmId}/channels/{id}.js create mode 100644 src/api/admin/v1/routes/realms/{realmId}/channels/{id}.yaml create mode 100644 src/tasks/admin/delete-channel.js create mode 100644 src/tasks/admin/delete-channel.spec.js diff --git a/src/api/admin/v1/routes/realms/{realmId}/channels/{id}.js b/src/api/admin/v1/routes/realms/{realmId}/channels/{id}.js new file mode 100644 index 0000000..a53953c --- /dev/null +++ b/src/api/admin/v1/routes/realms/{realmId}/channels/{id}.js @@ -0,0 +1,22 @@ +/** + * @param {{admin: AdminTasks}} tasks Tasks + * @returns {{delete: delete}} Method handlers + */ +module.exports = tasks => ({ + /** + * DELETE /realms/{realmId}/channels/{id} + * @param {object} request Request + * @param {object} response Response + */ + async delete(request, response) { + const {realmId, id} = request.params; + + const {ok, error} = await tasks.admin.deleteChannel({realmId, id}); + + if (!ok) { + throw error; + } + + response.status(204).send(); + }, +}); diff --git a/src/api/admin/v1/routes/realms/{realmId}/channels/{id}.yaml b/src/api/admin/v1/routes/realms/{realmId}/channels/{id}.yaml new file mode 100644 index 0000000..310e198 --- /dev/null +++ b/src/api/admin/v1/routes/realms/{realmId}/channels/{id}.yaml @@ -0,0 +1,25 @@ +/realms/{realmId}/channels/{id}: + parameters: + - $ref: '#/parameters/realmIdInPath' + - name: id + description: The channel id. + in: path + required: true + type: string + pattern: ^[\w-]+$ + + delete: + summary: Delete the channel referenced by id. + description: | + Delete the channel and all sub resources like subscriptions and messages. + tags: + - Channels + responses: + 204: + description: Channel has been deleted successfully. + 400: + description: Request validation failed. + 401: + description: The channel could not be deleted due to failed authorization. + 404: + description: The requested channel does not exist. diff --git a/src/bootstrap/tasks.js b/src/bootstrap/tasks.js index f386c2b..31a2bdf 100644 --- a/src/bootstrap/tasks.js +++ b/src/bootstrap/tasks.js @@ -43,6 +43,7 @@ module.exports = ({ authTokenRules, authRepository, channelRepository, + messageRepository, realmRepository, subscriptionRepository, userRepository, diff --git a/src/repositories/message.js b/src/repositories/message.js index cd9c41d..2d67b8f 100644 --- a/src/repositories/message.js +++ b/src/repositories/message.js @@ -64,5 +64,19 @@ module.exports = ({collection, createModel = createMessageModel}) => { return multiRealmRepo.find({...query}, {limit, offset, sort}); }, + + /** + * Remove all messages from the given channel. + * + * @param {string} realmId Realm context + * @param {string} channelId Id of channel to remove messages for + */ + async deleteAllByChannelId({realmId, channelId}) { + if (!channelId) { + throw new Error('Missing channel id.'); + } + + await multiRealmRepo.deleteMany({realmId, channelId}); + }, }; }; diff --git a/src/tasks/admin/delete-channel.js b/src/tasks/admin/delete-channel.js new file mode 100644 index 0000000..457a83c --- /dev/null +++ b/src/tasks/admin/delete-channel.js @@ -0,0 +1,49 @@ +const {success, failure} = require('../../lib/result'); +const taskError = require('../../lib/error/task'); +const createTaskError = require('../../lib/error/task'); + +/** + * Init delete channel task + * @param {RealmRepository} realmRepository Realm repository + * @param {ChannelRepository} channelRepository Channel repository + * @param {SubscriptionRepository} subscriptionRepository Subscription repository + * @param {MessageRepository} messageRepository Message repository + * @returns {Function} Task + */ +module.exports = ({ + realmRepository, + channelRepository, + subscriptionRepository, + messageRepository, +}) => + /** + * @function AdminTasks#deleteChannel + * @param {string} realmId Realm id + * @param {string} id Channel id + * @returns {Result} + */ + async ({realmId, id}) => { + const realm = await realmRepository.findOne({id: realmId}); + if (!realm) { + return failure(createTaskError({ + code: 'UnknownRealm', + message: 'Cannot lookup the given realm.', + })); + } + + const channel = await channelRepository.findOne({realmId, id}); + if (!channel) { + return failure(taskError({ + code: 'UnknownChannel', + message: 'Channel does not exists.', + })); + } + + await Promise.all([ + channelRepository.deleteOne({realmId, id}), + subscriptionRepository.deleteAllByChannelId({realmId, channelId: id}), + messageRepository.deleteAllByChannelId({realmId, channelId: id}), + ]); + + return success(channel); + }; diff --git a/src/tasks/admin/delete-channel.spec.js b/src/tasks/admin/delete-channel.spec.js new file mode 100644 index 0000000..4f7d133 --- /dev/null +++ b/src/tasks/admin/delete-channel.spec.js @@ -0,0 +1,84 @@ +const channelRepository = require('../../lib/test/mocks/repositories/channel'); +const realmRepository = require('../../lib/test/mocks/repositories/realm'); +const messageRepository = require('../../lib/test/mocks/repositories/message'); +const subscriptionRepository = require('../../lib/test/mocks/repositories/subscription'); +const initDeleteChannel = require('./delete-channel'); + +describe('The client deleteChannel task', () => { + let deleteChannel; + + beforeEach(() => { + channelRepository.deleteOne = jest.fn(); + messageRepository.deleteAllByChannelId = jest.fn(); + subscriptionRepository.deleteAllByChannelId = jest.fn(); + deleteChannel = initDeleteChannel({ + realmRepository, + messageRepository, + channelRepository, + subscriptionRepository + }); + }); + + describe('when called with unknown realm id', () => { + it('should fail with appropriate error', async () => { + const {ok, error} = await deleteChannel({realmId: realmRepository.unknownRealmId, id: channelRepository.knownChannelId}); + + expect(ok).toBe(false); + expect(error).toBeDefined(); + expect(error.code).toBe('UnknownRealm'); + }); + }); + + describe('when given an unknown channel id', () => { + it('should fail with appropriate error', async () => { + const {ok, error} = await deleteChannel({ + realmId: realmRepository.knownRealmId, + id: channelRepository.unknownChannelId, + }); + + expect(ok).toBe(false); + expect(error).toBeDefined(); + expect(error.code).toBe('UnknownChannel'); + }); + }); + + describe('when configured correctly', () => { + it('should delete the channel', async () => { + const {ok} = await deleteChannel({ + realmId: realmRepository.knownRealmId, + id: channelRepository.knownChannelId, + }); + + expect(ok).toBe(true); + expect(channelRepository.deleteOne).toHaveBeenCalled(); + }); + + it('should delete all subscriptions on a channel', async () => { + const subscription = subscriptionRepository.validSubscription; + await deleteChannel({ + realmId: subscription.realmId, + id: subscription.channelId, + }); + + const deleteMethod = subscriptionRepository.deleteAllByChannelId; + expect(deleteMethod).toHaveBeenCalledWith({ + realmId: subscription.realmId, + channelId: subscription.channelId + }); + }); + + it('should delete all messages on a channel', async () => { + const message = messageRepository.validMessage; + await deleteChannel({ + realmId: message.realmId, + id: message.channelId, + }); + + const deleteMethod = messageRepository.deleteAllByChannelId; + expect(deleteMethod).toHaveBeenCalledWith({ + realmId: message.realmId, + channelId: message.channelId + }); + }); + }); +}); diff --git a/src/tasks/admin/index.js b/src/tasks/admin/index.js index d7f9660..a479c19 100644 --- a/src/tasks/admin/index.js +++ b/src/tasks/admin/index.js @@ -3,6 +3,7 @@ const initCreateRealm = require('./create-realm'); const initCreateRealmToken = require('./create-realm-token'); const initCreateSubscription = require('./create-subscription'); const initCreateUser = require('./create-user'); +const initDeleteChannel = require('./delete-channel'); const initDeleteToken = require('./delete-token'); const initDeleteUser = require('./delete-user'); const initFetchRealm = require('./fetch-realm'); @@ -18,6 +19,7 @@ const initListUsers = require('./list-users'); * @param {RealmRepository} realmRepository The realm repository * @param {AuthRepository} authRepository The auth repository * @param {ChannelRepository} channelRepository The channel repository + * @param {MessageRepository} messageRepository The message repository * @param {RealtimeConnectionRepository} realtimeConnectionRepository The real-time connection repository * @param {SubscriptionRepository} subscriptionRepository The subscription repository * @param {UserRepository} userRepository The user repository @@ -28,6 +30,7 @@ module.exports = ({ authTokenRules, authRepository, channelRepository, + messageRepository, realmRepository, realtimeConnectionRepository, subscriptionRepository, @@ -46,6 +49,12 @@ module.exports = ({ subscriptionRepository, }), createUser: initCreateUser({userRepository, realmRepository}), + deleteChannel: initDeleteChannel({ + messageRepository, + realmRepository, + channelRepository, + subscriptionRepository, + }), deleteToken: initDeleteToken({ authRepository, realmRepository, From b46f12cd80092cff974f28591a2f73d27cbcc01d Mon Sep 17 00:00:00 2001 From: Alrik Zachert Date: Mon, 27 Mar 2023 22:38:03 +0200 Subject: [PATCH 3/5] Add support for deleting subscriptions via admin api. --- .../realms/{realmId}/subscriptions/{id}.js | 22 ++++++ .../realms/{realmId}/subscriptions/{id}.yaml | 25 +++++++ src/tasks/admin/delete-subscription.js | 44 ++++++++++++ src/tasks/admin/delete-subscription.spec.js | 68 +++++++++++++++++++ src/tasks/admin/index.js | 6 ++ 5 files changed, 165 insertions(+) create mode 100644 src/api/admin/v1/routes/realms/{realmId}/subscriptions/{id}.js create mode 100644 src/api/admin/v1/routes/realms/{realmId}/subscriptions/{id}.yaml create mode 100644 src/tasks/admin/delete-subscription.js create mode 100644 src/tasks/admin/delete-subscription.spec.js diff --git a/src/api/admin/v1/routes/realms/{realmId}/subscriptions/{id}.js b/src/api/admin/v1/routes/realms/{realmId}/subscriptions/{id}.js new file mode 100644 index 0000000..603c9b9 --- /dev/null +++ b/src/api/admin/v1/routes/realms/{realmId}/subscriptions/{id}.js @@ -0,0 +1,22 @@ +/** + * @param {{admin: AdminTasks}} tasks Tasks + * @returns {{delete: delete}} Method handlers + */ +module.exports = tasks => ({ + /** + * DELETE /realms/{realmId}/subscriptions/{id} + * @param {object} request Request + * @param {object} response Response + */ + async delete(request, response) { + const {realmId, id} = request.params; + + const {ok, error} = await tasks.admin.deleteSubscription({realmId, id}); + + if (!ok) { + throw error; + } + + response.status(204).send(); + }, +}); diff --git a/src/api/admin/v1/routes/realms/{realmId}/subscriptions/{id}.yaml b/src/api/admin/v1/routes/realms/{realmId}/subscriptions/{id}.yaml new file mode 100644 index 0000000..1482b07 --- /dev/null +++ b/src/api/admin/v1/routes/realms/{realmId}/subscriptions/{id}.yaml @@ -0,0 +1,25 @@ +/realms/{realmId}/subscriptions/{id}: + parameters: + - $ref: '#/parameters/realmIdInPath' + - name: id + description: The subscription id. + in: path + required: true + type: string + pattern: ^[\w-]+$ + + delete: + summary: Delete the subscription referenced by id. + description: | + Delete the subscription and emit a subscription sync event. + tags: + - Subscriptions + responses: + 204: + description: Subscription has been deleted successfully. + 400: + description: Request validation failed. + 401: + description: The subscription could not be deleted due to failed authorization. + 404: + description: The requested subscription does not exist. diff --git a/src/tasks/admin/delete-subscription.js b/src/tasks/admin/delete-subscription.js new file mode 100644 index 0000000..1e8fd59 --- /dev/null +++ b/src/tasks/admin/delete-subscription.js @@ -0,0 +1,44 @@ +const {success, failure} = require('../../lib/result'); +const taskError = require('../../lib/error/task'); +const createTaskError = require('../../lib/error/task'); + +/** + * Init delete subscription task + * @param {RealmRepository} realmRepository Realm repository + * @param {SubscriptionRepository} subscriptionRepository Subscription repository + * @param {CommonTasks#sendSubscriptionSyncMessage} sendSubscriptionSyncMessage Task for sending subscription sync + * @returns {AdminTasks#deleteSubscription} Task + */ +module.exports = ({ + realmRepository, + subscriptionRepository, + sendSubscriptionSyncMessage, +}) => + /** + * @function AdminTasks#deleteSubscription + * @param {string} realmId Realm id + * @param {string} id Subscription id + * @returns {Result} + */ + async ({realmId, id}) => { + const realm = await realmRepository.findOne({id: realmId}); + if (!realm) { + return failure(createTaskError({ + code: 'UnknownRealm', + message: 'Cannot lookup the given realm.', + })); + } + + const subscription = await subscriptionRepository.findOne({realmId, id}); + if (!subscription) { + return failure(taskError({ + code: 'UnknownSubscription', + message: 'Subscription does not exists.', + })); + } + + await subscriptionRepository.deleteOne({realmId, id}); + sendSubscriptionSyncMessage({subscription, action: 'deleted'}); + + return success(subscription); + }; diff --git a/src/tasks/admin/delete-subscription.spec.js b/src/tasks/admin/delete-subscription.spec.js new file mode 100644 index 0000000..70231b2 --- /dev/null +++ b/src/tasks/admin/delete-subscription.spec.js @@ -0,0 +1,68 @@ +const subscriptionRepository = require('../../lib/test/mocks/repositories/subscription'); +const realmRepository = require('../../lib/test/mocks/repositories/realm'); +const initDeleteSubscription = require('./delete-subscription'); + +describe('The admin deleteSubscription task', () => { + const sendSubscriptionSyncMessage = jest.fn(); + let deleteSubscription; + + beforeEach(() => { + subscriptionRepository.deleteOne = jest.fn(); + deleteSubscription = initDeleteSubscription({ + realmRepository, + subscriptionRepository, + sendSubscriptionSyncMessage, + }); + }); + + describe('when called with unknown realm id', () => { + it('should fail with appropriate error', async () => { + const {ok, error} = await deleteSubscription({ + realmId: realmRepository.unknownRealmId, + id: subscriptionRepository.knownSubscriptionId + }); + + expect(ok).toBe(false); + expect(error).toBeDefined(); + expect(error.code).toBe('UnknownRealm'); + }); + }); + + describe('when given an unknown subscription id', () => { + it('should fail with appropriate error', async () => { + const {ok, error} = await deleteSubscription({ + realmId: realmRepository.knownRealmId, + id: subscriptionRepository.unknownSubscriptionId, + }); + + expect(ok).toBe(false); + expect(error).toBeDefined(); + expect(error.code).toBe('UnknownSubscription'); + }); + }); + + describe('when configured correctly', () => { + it('should delete the subscription', async () => { + const {ok} = await deleteSubscription({ + realmId: realmRepository.knownRealmId, + id: subscriptionRepository.knownSubscriptionId, + }); + + expect(ok).toBe(true); + expect(subscriptionRepository.deleteOne).toHaveBeenCalled(); + }); + + it('should send a subscription deleted sync message', async () => { + const {ok, result} = await deleteSubscription({ + realmId: realmRepository.knownRealmId, + id: subscriptionRepository.knownSubscriptionId, + }); + + expect(ok).toBe(true); + expect(sendSubscriptionSyncMessage).toHaveBeenCalledWith({ + subscription: result, + action: 'deleted' + }); + }); + }); +}); diff --git a/src/tasks/admin/index.js b/src/tasks/admin/index.js index a479c19..4703787 100644 --- a/src/tasks/admin/index.js +++ b/src/tasks/admin/index.js @@ -4,6 +4,7 @@ const initCreateRealmToken = require('./create-realm-token'); const initCreateSubscription = require('./create-subscription'); const initCreateUser = require('./create-user'); const initDeleteChannel = require('./delete-channel'); +const initDeleteSubscription = require('./delete-subscription'); const initDeleteToken = require('./delete-token'); const initDeleteUser = require('./delete-user'); const initFetchRealm = require('./fetch-realm'); @@ -55,6 +56,11 @@ module.exports = ({ channelRepository, subscriptionRepository, }), + deleteSubscription: initDeleteSubscription({ + realmRepository, + subscriptionRepository, + sendSubscriptionSyncMessage + }), deleteToken: initDeleteToken({ authRepository, realmRepository, From 099bd84df84f4a40f1865045845472a0253fae8d Mon Sep 17 00:00:00 2001 From: Alrik Zachert Date: Mon, 27 Mar 2023 22:39:01 +0200 Subject: [PATCH 4/5] Fix coding style. --- src/tasks/admin/delete-channel.spec.js | 6 +++--- src/tasks/admin/delete-subscription.spec.js | 4 ++-- src/tasks/admin/index.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tasks/admin/delete-channel.spec.js b/src/tasks/admin/delete-channel.spec.js index 4f7d133..ccadbfa 100644 --- a/src/tasks/admin/delete-channel.spec.js +++ b/src/tasks/admin/delete-channel.spec.js @@ -15,7 +15,7 @@ describe('The client deleteChannel task', () => { realmRepository, messageRepository, channelRepository, - subscriptionRepository + subscriptionRepository, }); }); @@ -63,7 +63,7 @@ describe('The client deleteChannel task', () => { const deleteMethod = subscriptionRepository.deleteAllByChannelId; expect(deleteMethod).toHaveBeenCalledWith({ realmId: subscription.realmId, - channelId: subscription.channelId + channelId: subscription.channelId, }); }); @@ -77,7 +77,7 @@ describe('The client deleteChannel task', () => { const deleteMethod = messageRepository.deleteAllByChannelId; expect(deleteMethod).toHaveBeenCalledWith({ realmId: message.realmId, - channelId: message.channelId + channelId: message.channelId, }); }); }); diff --git a/src/tasks/admin/delete-subscription.spec.js b/src/tasks/admin/delete-subscription.spec.js index 70231b2..a25ace2 100644 --- a/src/tasks/admin/delete-subscription.spec.js +++ b/src/tasks/admin/delete-subscription.spec.js @@ -19,7 +19,7 @@ describe('The admin deleteSubscription task', () => { it('should fail with appropriate error', async () => { const {ok, error} = await deleteSubscription({ realmId: realmRepository.unknownRealmId, - id: subscriptionRepository.knownSubscriptionId + id: subscriptionRepository.knownSubscriptionId, }); expect(ok).toBe(false); @@ -61,7 +61,7 @@ describe('The admin deleteSubscription task', () => { expect(ok).toBe(true); expect(sendSubscriptionSyncMessage).toHaveBeenCalledWith({ subscription: result, - action: 'deleted' + action: 'deleted', }); }); }); diff --git a/src/tasks/admin/index.js b/src/tasks/admin/index.js index 4703787..d2a477d 100644 --- a/src/tasks/admin/index.js +++ b/src/tasks/admin/index.js @@ -59,7 +59,7 @@ module.exports = ({ deleteSubscription: initDeleteSubscription({ realmRepository, subscriptionRepository, - sendSubscriptionSyncMessage + sendSubscriptionSyncMessage, }), deleteToken: initDeleteToken({ authRepository, From fa0bcdceb1bb60e3a468ea2d29a91b195de4aff2 Mon Sep 17 00:00:00 2001 From: Alrik Zachert Date: Thu, 6 Jul 2023 15:12:11 +0200 Subject: [PATCH 5/5] Update CHANGELOG.md Co-authored-by: Henning --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 148fc9f..1c68586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Introduce new administrative channel management endpoints `/admin/v1/realms/{realmId}/channels`. - Introduce new administrative subscription management endpoints `/admin/v1/realms/{realmId}/subscriptions`. - Introduce new administrative endpoint for deleting auth tokens `DELETE /admin/v1/realms/{realmId}/tokens/{tokenId}` -- Introduce new administrative endpoint for deleting users `DELETE /admin/v1/realms/{realmId}/users/{userId}` +- Introduce new administrative endpoint for deleting channels `DELETE /admin/v1/realms/{realmId}/channels/{channelId}`. +- Introduce new administrative endpoint for deleting subscriptions `DELETE /admin/v1/realms/{realmId}/subscriptions/{subscriptionsId}`. +- Introduce new administrative endpoint for deleting users `DELETE /admin/v1/realms/{realmId}/users/{userId}`. ### Changed - Add support for upgraded verneMQ broker. This requires the acceptance of their [EULA](https://vernemq.com/end-user-license-agreement/),