diff --git a/redisinsight/api/src/modules/pub-sub/pub-sub.analytics.service.spec.ts b/redisinsight/api/src/modules/pub-sub/pub-sub.analytics.service.spec.ts
index 99757afd2b..fc0df51d06 100644
--- a/redisinsight/api/src/modules/pub-sub/pub-sub.analytics.service.spec.ts
+++ b/redisinsight/api/src/modules/pub-sub/pub-sub.analytics.service.spec.ts
@@ -3,6 +3,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
import { mockDatabase } from 'src/__mocks__';
import { TelemetryEvents } from 'src/constants';
import { PubSubAnalyticsService } from './pub-sub.analytics.service';
+import { SubscriptionType } from './constants';
const instanceId = mockDatabase.id;
@@ -45,15 +46,32 @@ describe('PubSubAnalyticsService', () => {
});
describe('sendChannelSubscribeEvent', () => {
- it('should emit sendChannelSubscribe event', () => {
+ it('should emit sendChannelSubscribe event for all channels', () => {
service.sendChannelSubscribeEvent(
instanceId,
+ [{ channel: '*', type: SubscriptionType.Subscribe }],
);
expect(sendEventMethod).toHaveBeenCalledWith(
TelemetryEvents.PubSubChannelSubscribed,
{
databaseId: instanceId,
+ allChannels: 'yes',
+ },
+ );
+ });
+
+ it('should emit sendChannelSubscribe event not for all channels', () => {
+ service.sendChannelSubscribeEvent(
+ instanceId,
+ [{ channel: '1', type: SubscriptionType.Subscribe }],
+ );
+
+ expect(sendEventMethod).toHaveBeenCalledWith(
+ TelemetryEvents.PubSubChannelSubscribed,
+ {
+ databaseId: instanceId,
+ allChannels: 'no',
},
);
});
diff --git a/redisinsight/api/src/modules/pub-sub/pub-sub.analytics.service.ts b/redisinsight/api/src/modules/pub-sub/pub-sub.analytics.service.ts
index b3c6cc64e8..0ff74f63c7 100644
--- a/redisinsight/api/src/modules/pub-sub/pub-sub.analytics.service.ts
+++ b/redisinsight/api/src/modules/pub-sub/pub-sub.analytics.service.ts
@@ -1,9 +1,11 @@
import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
-import { TelemetryEvents } from 'src/constants';
+import { some } from 'lodash';
+import { DEFAULT_MATCH, TelemetryEvents } from 'src/constants';
import { TelemetryBaseService } from 'src/modules/analytics/telemetry.base.service';
import { CommandExecutionStatus } from 'src/modules/cli/dto/cli.dto';
import { RedisError, ReplyError } from 'src/models';
+import { SubscriptionDto } from './dto';
export interface IExecResult {
response: any;
@@ -31,12 +33,13 @@ export class PubSubAnalyticsService extends TelemetryBaseService {
}
}
- sendChannelSubscribeEvent(databaseId: string): void {
+ sendChannelSubscribeEvent(databaseId: string, subs: SubscriptionDto[]): void {
try {
this.sendEvent(
TelemetryEvents.PubSubChannelSubscribed,
{
databaseId,
+ allChannels: some(subs, { channel: DEFAULT_MATCH }) ? 'yes' : 'no',
},
);
} catch (e) {
diff --git a/redisinsight/api/src/modules/pub-sub/pub-sub.service.ts b/redisinsight/api/src/modules/pub-sub/pub-sub.service.ts
index 2aea95c19a..14195d65d3 100644
--- a/redisinsight/api/src/modules/pub-sub/pub-sub.service.ts
+++ b/redisinsight/api/src/modules/pub-sub/pub-sub.service.ts
@@ -34,7 +34,7 @@ export class PubSubService {
await Promise.all(dto.subscriptions.map((subDto) => session.subscribe(
this.subscriptionProvider.createSubscription(userClient, subDto),
)));
- this.analyticsService.sendChannelSubscribeEvent(userClient.getDatabaseId());
+ this.analyticsService.sendChannelSubscribeEvent(userClient.getDatabaseId(), dto.subscriptions);
} catch (e) {
this.logger.error('Unable to create subscriptions', e);
diff --git a/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx b/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx
index 39b2f25529..99239eb409 100644
--- a/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx
+++ b/redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx
@@ -3,7 +3,6 @@ import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useParams } from 'react-router-dom'
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
-import { SubscriptionType } from 'uiSrc/constants/pubSub'
import { sendEventTelemetry, sendPageViewTelemetry, TelemetryEvent, TelemetryPageView } from 'uiSrc/telemetry'
import { formatLongName, getDbIndex, setTitle } from 'uiSrc/utils'
@@ -15,8 +14,6 @@ import { MessagesListWrapper, PublishMessage, SubscriptionPanel } from './compon
import styles from './styles.module.scss'
-export const PUB_SUB_DEFAULT_CHANNEL = { channel: '*', type: SubscriptionType.PSubscribe }
-
const PubSubPage = () => {
const { name: connectedInstanceName, db } = useSelector(connectedInstanceSelector)
const { instanceId } = useParams<{ instanceId: string }>()
diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/SubscriptionPanel.spec.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/SubscriptionPanel.spec.tsx
new file mode 100644
index 0000000000..fd63fe312d
--- /dev/null
+++ b/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/SubscriptionPanel.spec.tsx
@@ -0,0 +1,30 @@
+import React from 'react'
+import { fireEvent } from '@testing-library/react'
+import { cloneDeep } from 'lodash'
+import { toggleSubscribeTriggerPubSub } from 'uiSrc/slices/pubsub/pubsub'
+import { cleanup, clearStoreActions, mockedStore, render, screen } from 'uiSrc/utils/test-utils'
+
+import SubscriptionPanel from './SubscriptionPanel'
+
+let store: typeof mockedStore
+
+beforeEach(() => {
+ cleanup()
+ store = cloneDeep(mockedStore)
+ store.clearActions()
+})
+
+describe('SubscriptionPanel', () => {
+ it('should render', () => {
+ expect(render()).toBeTruthy()
+ })
+
+ it('should dispatch subscribe action after toggle subscribe button', () => {
+ render()
+ const expectedActions = [toggleSubscribeTriggerPubSub('1 2 3')]
+ fireEvent.change(screen.getByTestId('channels-input'), { target: { value: '1 2 3' } })
+ fireEvent.click(screen.getByTestId('subscribe-btn'))
+
+ expect(clearStoreActions(store.getActions())).toEqual(clearStoreActions(expectedActions))
+ })
+})
diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/SubscriptionPanel.tsx b/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/SubscriptionPanel.tsx
index b9134fadb3..7086a56e5d 100644
--- a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/SubscriptionPanel.tsx
+++ b/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/SubscriptionPanel.tsx
@@ -1,11 +1,10 @@
-import { EuiButton, EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText, EuiToolTip } from '@elastic/eui'
+import { EuiButton, EuiButtonIcon, EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText, EuiToolTip } from '@elastic/eui'
import cx from 'classnames'
-import React, { useContext } from 'react'
+import React, { useContext, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useParams } from 'react-router-dom'
import { Theme } from 'uiSrc/constants'
import { ThemeContext } from 'uiSrc/contexts/themeContext'
-import { PUB_SUB_DEFAULT_CHANNEL } from 'uiSrc/pages/pub-sub/PubSubPage'
import { clearPubSubMessages, pubSubSelector, toggleSubscribeTriggerPubSub } from 'uiSrc/slices/pubsub/pubsub'
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
@@ -25,8 +24,10 @@ const SubscriptionPanel = () => {
const { instanceId = '' } = useParams<{ instanceId: string }>()
+ const [channels, setChannels] = useState('')
+
const toggleSubscribe = () => {
- dispatch(toggleSubscribeTriggerPubSub([PUB_SUB_DEFAULT_CHANNEL]))
+ dispatch(toggleSubscribeTriggerPubSub(channels))
}
const onClickClear = () => {
@@ -70,21 +71,17 @@ const SubscriptionPanel = () => {
- {!!messages.length && (
-
-
-
-
-
- )}
+
+ setChannels(e.target.value)}
+ placeholder="Enter Channel to Subscribe"
+ aria-label="channel names for filtering"
+ data-testid="channels-input"
+ />
+
{
{ isSubscribed ? 'Unsubscribe' : 'Subscribe' }
+ {!!messages.length && (
+
+
+
+
+
+ )}
diff --git a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/styles.module.scss b/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/styles.module.scss
index e7b9089387..a1325738db 100644
--- a/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/styles.module.scss
+++ b/redisinsight/ui/src/pages/pub-sub/components/subscription-panel/styles.module.scss
@@ -20,3 +20,15 @@
height: 18px;
}
}
+
+.channels {
+ margin-right: 8px;
+
+ input {
+ width: 210px;
+ }
+
+ :global(.euiFormControlLayout), input {
+ height: 30px;
+ }
+}
diff --git a/redisinsight/ui/src/slices/pubsub/pubsub.ts b/redisinsight/ui/src/slices/pubsub/pubsub.ts
index 8ce4fc79c6..55b48b1321 100644
--- a/redisinsight/ui/src/slices/pubsub/pubsub.ts
+++ b/redisinsight/ui/src/slices/pubsub/pubsub.ts
@@ -6,7 +6,8 @@ import { addErrorNotification } from 'uiSrc/slices/app/notifications'
import { StatePubSub } from 'uiSrc/slices/interfaces/pubsub'
import { AppDispatch, RootState } from 'uiSrc/slices/store'
import { getApiErrorMessage, getUrl, isStatusSuccessful } from 'uiSrc/utils'
-import { SubscriptionDto } from 'apiSrc/modules/pub-sub/dto/subscription.dto'
+import { DEFAULT_SEARCH_MATCH } from 'uiSrc/constants/api'
+import { SubscriptionType } from 'uiSrc/constants/pubSub'
import { MessagesResponse } from 'apiSrc/modules/pub-sub/dto/messages.response'
import { PublishResponse } from 'apiSrc/modules/pub-sub/dto/publish.response'
@@ -32,9 +33,15 @@ const pubSubSlice = createSlice({
setPubSubConnected: (state, { payload }: PayloadAction) => {
state.isConnected = payload
},
- toggleSubscribeTriggerPubSub: (state, { payload }: PayloadAction) => {
+ toggleSubscribeTriggerPubSub: (state, { payload }: PayloadAction) => {
+ const channels = payload.trim() || DEFAULT_SEARCH_MATCH
+ const subs = channels.split(' ').map((channel) => ({
+ channel,
+ type: SubscriptionType.PSubscribe,
+ }))
+
state.isSubscribeTriggered = !state.isSubscribeTriggered
- state.subscriptions = payload
+ state.subscriptions = subs
},
setIsPubSubSubscribed: (state) => {
state.isSubscribed = true
diff --git a/redisinsight/ui/src/slices/tests/pubsub/pubsub.spec.ts b/redisinsight/ui/src/slices/tests/pubsub/pubsub.spec.ts
index 0853c5367d..d1fb1a1ca5 100644
--- a/redisinsight/ui/src/slices/tests/pubsub/pubsub.spec.ts
+++ b/redisinsight/ui/src/slices/tests/pubsub/pubsub.spec.ts
@@ -64,17 +64,37 @@ describe('pubsub slice', () => {
describe('toggleSubscribeTriggerPubSub', () => {
it('should properly set state', () => {
- const subscriptions = [{ channel: '1', type: 'ss' }]
+ const channels = '1 * 3'
// Arrange
const state = {
...initialState,
isSubscribeTriggered: !initialState.isSubscribeTriggered,
- subscriptions
+ subscriptions: [{ channel: '1', type: 'p' }, { channel: '*', type: 'p' }, { channel: '3', type: 'p' }]
}
// Act
- const nextState = reducer(initialState, toggleSubscribeTriggerPubSub(subscriptions))
+ const nextState = reducer(initialState, toggleSubscribeTriggerPubSub(channels))
+
+ // Assert
+ const rootState = Object.assign(initialStateDefault, {
+ pubsub: nextState,
+ })
+ expect(pubSubSelector(rootState)).toEqual(state)
+ })
+
+ it('should properly set state for empty channels', () => {
+ const channels = ''
+
+ // Arrange
+ const state = {
+ ...initialState,
+ isSubscribeTriggered: !initialState.isSubscribeTriggered,
+ subscriptions: [{ channel: '*', type: 'p' }]
+ }
+
+ // Act
+ const nextState = reducer(initialState, toggleSubscribeTriggerPubSub(channels))
// Assert
const rootState = Object.assign(initialStateDefault, {
diff --git a/tests/e2e/pageObjects/pub-sub-page.ts b/tests/e2e/pageObjects/pub-sub-page.ts
index edcca0859d..84986cfc12 100644
--- a/tests/e2e/pageObjects/pub-sub-page.ts
+++ b/tests/e2e/pageObjects/pub-sub-page.ts
@@ -27,6 +27,7 @@ export class PubSubPage extends InstancePage {
//INPUTS
channelNameInput = Selector('[data-testid=field-channel-name]');
messageInput = Selector('[data-testid=field-message]');
+ channelsSubscribeInput = Selector('[data-testid=channels-input]');
/**
* Publish message in pubsub
diff --git a/tests/e2e/tests/web/regression/pub-sub/pub-sub-oss-cluster-7.ts b/tests/e2e/tests/web/regression/pub-sub/pub-sub-oss-cluster-7.ts
index a26998c679..1fbaffbbe3 100644
--- a/tests/e2e/tests/web/regression/pub-sub/pub-sub-oss-cluster-7.ts
+++ b/tests/e2e/tests/web/regression/pub-sub/pub-sub-oss-cluster-7.ts
@@ -12,19 +12,20 @@ const databaseAPIRequests = new DatabaseAPIRequests();
fixture `PubSub OSS Cluster 7 tests`
.meta({ type: 'regression' })
- .page(commonUrl);
+ .page(commonUrl)
-test
- .before(async t => {
+ .beforeEach(async t => {
await databaseHelper.acceptLicenseTerms();
await databaseAPIRequests.addNewOSSClusterDatabaseApi(ossClusterConfig);
await myRedisDatabasePage.reloadPage();
await myRedisDatabasePage.clickOnDBByName(ossClusterConfig.ossClusterDatabaseName);
await t.click(myRedisDatabasePage.NavigationPanel.pubSubButton);
})
- .after(async() => {
+ .afterEach(async() => {
await databaseAPIRequests.deleteOSSClusterDatabaseApi(ossClusterConfig);
- })
+ });
+test
+
.meta({ rte: rte.ossCluster })('Verify that SPUBLISH message is displayed for OSS Cluster 7 database', async t => {
await t.expect(pubSubPage.ossClusterEmptyMessage.exists).ok('SPUBLISH message not displayed');
// Verify that user can see published messages for OSS Cluster 7
@@ -54,3 +55,16 @@ test
await pubSubPage.Cli.sendCommandInCli('10 spublish channel oss_cluster_message_spublish');
await verifyMessageDisplayingInPubSub('oss_cluster_message_spublish', false);
});
+
+test.meta({ rte: rte.ossCluster })('Verify that PSUBSCRIBE works, that user can specify channel name to subscribe', async t => {
+ const channelsName = 'first second third';
+ await t.typeText(pubSubPage.channelsSubscribeInput, channelsName, { replace: true });
+ await t.click(pubSubPage.subscribeButton);
+ await t.expect(pubSubPage.channelsSubscribeInput.hasAttribute('disabled')).ok('the field is not disabled after subscribe');
+ await pubSubPage.publishMessage(channelsName.split(' ')[0], 'published message');
+ await verifyMessageDisplayingInPubSub('published message', true);
+ await pubSubPage.publishMessage(channelsName.split(' ')[1], 'second message');
+ await verifyMessageDisplayingInPubSub('second message', true);
+ await pubSubPage.publishMessage('not exist', 'not exist message');
+ await verifyMessageDisplayingInPubSub('not exist message', false);
+});