Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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',
},
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion redisinsight/api/src/modules/pub-sub/pub-sub.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 0 additions & 3 deletions redisinsight/ui/src/pages/pub-sub/PubSubPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -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 }>()
Expand Down
Original file line number Diff line number Diff line change
@@ -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(<SubscriptionPanel />)).toBeTruthy()
})

it('should dispatch subscribe action after toggle subscribe button', () => {
render(<SubscriptionPanel />)
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))
})
})
Original file line number Diff line number Diff line change
@@ -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'

Expand All @@ -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 = () => {
Expand Down Expand Up @@ -70,21 +71,17 @@ const SubscriptionPanel = () => {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup alignItems="center" gutterSize="none" responsive={false}>
{!!messages.length && (
<EuiFlexItem grow={false} style={{ marginRight: 12 }}>
<EuiToolTip
content="Clear Messages"
anchorClassName={cx('inline-flex')}
>
<EuiButtonIcon
iconType="eraser"
onClick={onClickClear}
aria-label="clear pub sub"
data-testid="clear-pubsub-btn"
/>
</EuiToolTip>
</EuiFlexItem>
)}
<EuiFlexItem grow={false} className={styles.channels}>
<EuiFieldText
value={channels}
disabled={isSubscribed}
className={styles.channels}
onChange={(e) => setChannels(e.target.value)}
placeholder="Enter Channel to Subscribe"
aria-label="channel names for filtering"
data-testid="channels-input"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
fill={!isSubscribed}
Expand All @@ -100,6 +97,21 @@ const SubscriptionPanel = () => {
{ isSubscribed ? 'Unsubscribe' : 'Subscribe' }
</EuiButton>
</EuiFlexItem>
{!!messages.length && (
<EuiFlexItem grow={false} style={{ marginLeft: 8 }}>
<EuiToolTip
content="Clear Messages"
anchorClassName={cx('inline-flex')}
>
<EuiButtonIcon
iconType="eraser"
onClick={onClickClear}
aria-label="clear pub sub"
data-testid="clear-pubsub-btn"
/>
</EuiToolTip>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@
height: 18px;
}
}

.channels {
margin-right: 8px;

input {
width: 210px;
}

:global(.euiFormControlLayout), input {
height: 30px;
}
}
13 changes: 10 additions & 3 deletions redisinsight/ui/src/slices/pubsub/pubsub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -32,9 +33,15 @@ const pubSubSlice = createSlice({
setPubSubConnected: (state, { payload }: PayloadAction<boolean>) => {
state.isConnected = payload
},
toggleSubscribeTriggerPubSub: (state, { payload }: PayloadAction<SubscriptionDto[]>) => {
toggleSubscribeTriggerPubSub: (state, { payload }: PayloadAction<string>) => {
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
Expand Down
26 changes: 23 additions & 3 deletions redisinsight/ui/src/slices/tests/pubsub/pubsub.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down
1 change: 1 addition & 0 deletions tests/e2e/pageObjects/pub-sub-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 19 additions & 5 deletions tests/e2e/tests/web/regression/pub-sub/pub-sub-oss-cluster-7.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
});
Loading