Skip to content

Commit d97025c

Browse files
authored
RI-7027: Add JSON key -> Create free db dialog (#266)
* when json module is missing, instead of opening the try-free link, change to open the oauth sso dialog by passing the callback fn to the helper text
1 parent 6aa6643 commit d97025c

File tree

8 files changed

+164
-53
lines changed

8 files changed

+164
-53
lines changed

src/webviews/src/constants/cloud/source.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export enum OAuthSocialSource {
1010
BrowserContentMenu = 'browser content menu',
1111
BrowserFiltering = 'browser filtering',
1212
BrowserSearch = 'browser search',
13+
BrowserRedisJSON = 'browser RedisJSON',
1314
RediSearch = 'workbench RediSearch',
1415
RedisJSON = 'workbench RedisJSON',
1516
RedisTimeSeries = 'workbench RedisTimeSeries',

src/webviews/src/constants/window/helpTexts.tsx

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,27 @@ import { EXTERNAL_LINKS, UTM_CAMPAIGNS } from 'uiSrc/constants'
88
import styles from 'uiSrc/components/popover-delete/styles.module.scss'
99

1010
export const helpTexts = {
11-
REJSON_SHOULD_BE_LOADED: (
11+
REJSON_SHOULD_BE_LOADED: (onFreeTrialDbClick: () => void) => (
1212
<>
13-
{l10n.t('This database does not support the JSON data structure. Learn more about JSON support ')}
13+
{l10n.t(
14+
'This database does not support the JSON data structure. Learn more about JSON support ',
15+
)}
1416
<Link
15-
to={getUtmExternalLink(EXTERNAL_LINKS.jsonModule, { campaign: UTM_CAMPAIGNS.tutorials })}
17+
to={getUtmExternalLink(EXTERNAL_LINKS.jsonModule, {
18+
campaign: UTM_CAMPAIGNS.tutorials,
19+
})}
1620
className="underline hover:no-underline"
1721
target="_blank"
18-
data-test-subj="no-json-module-info"
22+
data-testid="no-json-module-info"
1923
>
2024
{l10n.t('here. ')}
2125
</Link>
2226
{l10n.t('You can also create a ')}
2327
<Link
24-
to={getUtmExternalLink(EXTERNAL_LINKS.tryFree, { campaign: UTM_CAMPAIGNS.redisjson })}
28+
onClick={onFreeTrialDbClick}
2529
className="underline hover:no-underline"
26-
target="_blank"
27-
data-test-subj="no-json-module-try-free"
30+
data-testid="no-json-module-create-free-db"
31+
to=""
2832
>
2933
{l10n.t('free trial Redis Cloud database')}
3034
</Link>
@@ -35,15 +39,22 @@ export const helpTexts = {
3539
<div className={styles.appendInfo}>
3640
<VscWarning className="mr-1 mt-1" />
3741
<span>
38-
{l10n.t('If you remove the single {0}, the whole Key will be deleted.', fieldType)}
42+
{l10n.t(
43+
'If you remove the single {0}, the whole Key will be deleted.',
44+
fieldType,
45+
)}
3946
</span>
4047
</div>
4148
),
4249
REMOVING_MULTIPLE_ELEMENTS_NOT_SUPPORT: (
4350
<>
44-
{l10n.t('Removing multiple elements is available for Redis databases v. 6.2 or later. Update your Redis database or create a new ')}
51+
{l10n.t(
52+
'Removing multiple elements is available for Redis databases v. 6.2 or later. Update your Redis database or create a new ',
53+
)}
4554
<Link
46-
to={getUtmExternalLink(EXTERNAL_LINKS.tryFree, { campaign: UTM_CAMPAIGNS.redisLatest })}
55+
to={getUtmExternalLink(EXTERNAL_LINKS.tryFree, {
56+
campaign: UTM_CAMPAIGNS.redisLatest,
57+
})}
4758
className="underline hover:no-underline"
4859
target="_blank"
4960
data-test-subj="no-json-module-try-free"

src/webviews/src/modules/add-key/AddKey.spec.tsx

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -29,36 +29,4 @@ describe('AddKey', () => {
2929

3030
expect((screen.getByTestId('select-key-type') as HTMLInputElement).value).toBe(ADD_KEY_TYPE_OPTIONS[0].value)
3131
})
32-
33-
// it('should show text if db not contains ReJSON module', async () => {
34-
// render(<AddKey
35-
// />)
36-
37-
// fireEvent.click(screen.getByTestId('select-key-type'))
38-
// await waitFor(() => {
39-
// fireEvent.click(
40-
// screen.queryByText('JSON') || document,
41-
// )
42-
// })
43-
44-
// expect(screen.getByTestId('json-not-loaded-text')).toBeInTheDocument()
45-
// })
46-
47-
// it('should not show text if db contains ReJSON module', async () => {
48-
// (connectedInstanceSelector as vi.Mock).mockImplementation(() => ({
49-
// modules: [{ name: RedisDefaultModules.FT }, { name: RedisDefaultModules.ReJSON }],
50-
// }))
51-
52-
// render(<AddKey
53-
// />)
54-
55-
// fireEvent.click(screen.getByTestId('select-key-type'))
56-
// await waitFor(() => {
57-
// fireEvent.click(
58-
// screen.queryByText('JSON') || document,
59-
// )
60-
// })
61-
62-
// expect(screen.queryByTestId('json-not-loaded-text')).not.toBeInTheDocument()
63-
// })
6432
})

src/webviews/src/modules/add-key/AddKey.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { KeyTypes, SelectedKeyActionType, VscodeMessageAction } from 'uiSrc/cons
44

55
import { Maybe } from 'uiSrc/interfaces'
66
import { vscodeApi } from 'uiSrc/services'
7-
import { useDatabasesStore } from 'uiSrc/store'
87
import { getGroupTypeDisplay, stringToBuffer } from 'uiSrc/utils'
98

109
import AddKeyCommonFields from './components/AddKeyCommonFields/AddKeyCommonFields'
@@ -23,7 +22,6 @@ import styles from './styles.module.scss'
2322
export const AddKey = () => {
2423
const loading = useKeysInContext((state) => state.addKeyLoading)
2524
const keysApi = useKeysApi()
26-
const database = useDatabasesStore((state) => state.connectedDatabase)
2725

2826
useEffect(
2927
() =>

src/webviews/src/modules/add-key/components/AddKeyReJSON/AddKeyReJSON.spec.tsx

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,44 @@ import React from 'react'
22
import { instance, mock } from 'ts-mockito'
33

44
import * as utils from 'uiSrc/utils'
5-
import { render } from 'testSrc/helpers'
5+
import {
6+
Database,
7+
DatabasesStore,
8+
initialDatabasesState,
9+
useDatabasesStore,
10+
} from 'uiSrc/store'
11+
import { ConnectionType, Nullable, RedisDefaultModules } from 'uiSrc/interfaces'
12+
import { OAuthSsoDialog } from 'uiSrc/modules/oauth'
13+
import { fireEvent, render, screen } from 'testSrc/helpers'
614

715
import { AddKeyReJSON, Props } from './AddKeyReJSON'
816

17+
const mockConnectedDatabase: Nullable<Database> = {
18+
...initialDatabasesState.connectedDatabase,
19+
id: 'mocked-id',
20+
name: 'mocked-database',
21+
host: '127.0.0.1',
22+
port: 6379,
23+
modules: [],
24+
username: 'mocked-user',
25+
password: 'mocked-password',
26+
tls: false,
27+
db: 0,
28+
lastConnection: new Date(),
29+
provider: 'RE_CLOUD',
30+
connectionType: ConnectionType.Standalone,
31+
version: null,
32+
}
33+
34+
const customState: DatabasesStore = {
35+
...initialDatabasesState,
36+
connectedDatabase: mockConnectedDatabase,
37+
}
38+
39+
beforeEach(() => {
40+
useDatabasesStore.setState(customState)
41+
})
42+
943
const mockedProps = mock<Props>()
1044

1145
vi.spyOn(utils, 'sendEventTelemetry')
@@ -16,6 +50,73 @@ describe('AddKeyReJSON', () => {
1650
})
1751

1852
it('should render', () => {
19-
expect(render(<AddKeyReJSON {...instance(mockedProps)} />, { withRouter: true })).toBeTruthy()
53+
expect(
54+
render(<AddKeyReJSON {...instance(mockedProps)} />, { withRouter: true }),
55+
).toBeTruthy()
56+
})
57+
58+
it('should not display the JSON not supported text when JSON module is available', () => {
59+
useDatabasesStore.setState({
60+
...customState,
61+
connectedDatabase: {
62+
...mockConnectedDatabase,
63+
modules: [
64+
{
65+
name: RedisDefaultModules.ReJSON,
66+
},
67+
],
68+
},
69+
})
70+
71+
const { queryByTestId } = render(
72+
<AddKeyReJSON {...instance(mockedProps)} />,
73+
{ withRouter: true },
74+
)
75+
76+
const textId = 'json-not-loaded-text'
77+
expect(queryByTestId(textId)).not.toBeInTheDocument()
78+
})
79+
80+
it('should display the JSON not supported text when JSON module is not available', () => {
81+
useDatabasesStore.setState({
82+
...customState,
83+
connectedDatabase: {
84+
...mockConnectedDatabase,
85+
modules: [],
86+
},
87+
})
88+
89+
const { queryByTestId } = render(
90+
<AddKeyReJSON {...instance(mockedProps)} />,
91+
{ withRouter: true },
92+
)
93+
94+
const textId = 'json-not-loaded-text'
95+
expect(queryByTestId(textId)).toBeInTheDocument()
96+
})
97+
98+
it('should open sso oauth dialog when free trial redis cloud database link clicked', () => {
99+
useDatabasesStore.setState({
100+
...customState,
101+
connectedDatabase: {
102+
...mockConnectedDatabase,
103+
modules: [],
104+
},
105+
})
106+
107+
const { queryByTestId } = render(
108+
<AddKeyReJSON {...instance(mockedProps)} />,
109+
{ withRouter: true },
110+
)
111+
render(<OAuthSsoDialog />)
112+
113+
const oauthDialogId = 'social-oauth-dialog'
114+
expect(screen.queryByTestId(oauthDialogId)).not.toBeInTheDocument()
115+
116+
const linkId = 'no-json-module-create-free-db'
117+
expect(queryByTestId(linkId)).toBeInTheDocument()
118+
fireEvent.click(queryByTestId(linkId) as HTMLAnchorElement)
119+
120+
expect(screen.queryByTestId(oauthDialogId)).toBeInTheDocument()
20121
})
21122
})

src/webviews/src/modules/add-key/components/AddKeyReJSON/AddKeyReJSON.tsx

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,27 @@ import { VSCodeButton } from '@vscode/webview-ui-toolkit/react'
44
import Popup from 'reactjs-popup'
55

66
import { MonacoJson } from 'uiSrc/components/monaco-editor'
7-
import { getRequiredFieldsText, isContainJSONModule, sendEventTelemetry, stringToBuffer, TelemetryEvent } from 'uiSrc/utils'
7+
import {
8+
getRequiredFieldsText,
9+
isContainJSONModule,
10+
sendEventTelemetry,
11+
stringToBuffer,
12+
TelemetryEvent,
13+
} from 'uiSrc/utils'
814
import { Maybe } from 'uiSrc/interfaces'
9-
import { useKeysApi, useKeysInContext } from 'uiSrc/modules/keys-tree/hooks/useKeys'
15+
import {
16+
useKeysApi,
17+
useKeysInContext,
18+
} from 'uiSrc/modules/keys-tree/hooks/useKeys'
1019
import {
1120
AddJSONFormConfig as config,
1221
helpTexts,
1322
KeyTypes,
23+
OAuthSocialAction,
24+
OAuthSocialSource,
1425
} from 'uiSrc/constants'
1526
import { CreateRejsonRlWithExpireDto } from 'uiSrc/modules/keys-tree/hooks/interface'
16-
import { useDatabasesStore } from 'uiSrc/store'
27+
import { useDatabasesStore, useOAuthStore } from 'uiSrc/store'
1728
import { UploadFile } from 'uiSrc/components'
1829
import { JSONErrors } from 'uiSrc/modules/key-details/components/rejson-details/constants'
1930

@@ -24,6 +35,11 @@ export interface Props {
2435
}
2536

2637
export const AddKeyReJSON = (props: Props) => {
38+
const { setSSOFlow, setSocialDialogState } = useOAuthStore((state) => ({
39+
setSSOFlow: state.setSSOFlow,
40+
setSocialDialogState: state.setSocialDialogState,
41+
}))
42+
2743
const { keyName = '', keyTTL, onClose } = props
2844
const loading = useKeysInContext((state) => state.addKeyLoading)
2945
const modules = useDatabasesStore((state) => state.connectedDatabase?.modules)
@@ -76,6 +92,17 @@ export const AddKeyReJSON = (props: Props) => {
7692
})
7793
}, [databaseId])
7894

95+
const onFreeTrialDbClick = () => {
96+
const source = OAuthSocialSource.BrowserRedisJSON
97+
sendEventTelemetry({
98+
event: TelemetryEvent.CLOUD_FREE_DATABASE_CLICKED,
99+
eventData: { source },
100+
})
101+
102+
setSSOFlow(OAuthSocialAction.Create)
103+
setSocialDialogState(source)
104+
}
105+
79106
const SubmitBtn = () => (
80107
<VSCodeButton
81108
onClick={submitData}
@@ -96,15 +123,17 @@ export const AddKeyReJSON = (props: Props) => {
96123
<form onSubmit={onFormSubmit}>
97124
{!isJsonLoaded && (
98125
<span className="block pb-3" data-testid="json-not-loaded-text">
99-
{helpTexts.REJSON_SHOULD_BE_LOADED}
126+
{helpTexts.REJSON_SHOULD_BE_LOADED(onFreeTrialDbClick)}
100127
</span>
101128
)}
102129
<>
103130
<div className="pb-2 font-bold uppercase">{config.value.label}</div>
104131
<MonacoJson
105132
value={ReJSONValue}
106133
onChange={setReJSONValue}
107-
wrapperClassName={!isJsonLoaded ? 'h-[calc(100vh-340px)]' : 'h-[calc(100vh-300px)]'}
134+
wrapperClassName={
135+
!isJsonLoaded ? 'h-[calc(100vh-340px)]' : 'h-[calc(100vh-300px)]'
136+
}
108137
disabled={loading}
109138
data-testid="json-value"
110139
/>

src/webviews/src/modules/oauth/oauth-create-free-db/OAuthCreateFreeDb.spec.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,14 @@ describe('OAuthCreateFreeDb', () => {
8181

8282
// component is initialized in the document, but the state is not updated yet to show the dialog
8383
render(<OAuthSsoDialog />)
84-
expect(screen.queryByTestId('social-oauth-dialog')).not.toBeInTheDocument()
84+
const oauthDialogId = 'social-oauth-dialog'
85+
expect(screen.queryByTestId(oauthDialogId)).not.toBeInTheDocument()
8586

8687
const regularCreateBtn = queryByTestId('create-free-db-btn')
8788
expect(regularCreateBtn).toBeInTheDocument()
8889

8990
fireEvent.click(regularCreateBtn as HTMLButtonElement)
9091

91-
expect(screen.queryByTestId('social-oauth-dialog')).toBeInTheDocument()
92+
expect(screen.queryByTestId(oauthDialogId)).toBeInTheDocument()
9293
})
9394
})

src/webviews/src/pages/AddKeyPage/AddKeyPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import React, { FC } from 'react'
22
import { AddKey } from 'uiSrc/modules'
33
import { KeysStoreProvider } from 'uiSrc/modules/keys-tree/hooks/useKeys'
4+
import { OAuthSsoDialog } from 'uiSrc/modules/oauth'
45
import { ContextStoreProvider } from 'uiSrc/store'
56

67
export const AddKeyPage: FC<any> = () => (
78
<div className="flex h-full w-full p-4 overflow-x-auto" data-testid="panel-view-page">
89
<ContextStoreProvider>
910
<KeysStoreProvider>
1011
<AddKey />
12+
<OAuthSsoDialog />
1113
</KeysStoreProvider>
1214
</ContextStoreProvider>
1315
</div>

0 commit comments

Comments
 (0)