Skip to content

Commit f7ea5fa

Browse files
committed
#RI-5376 - update parsing redis url
1 parent c9e0372 commit f7ea5fa

File tree

5 files changed

+162
-70
lines changed

5 files changed

+162
-70
lines changed

redisinsight/ui/src/components/global-url-handler/GlobalUrlHandler.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { useHistory, useLocation } from 'react-router-dom'
22
import { useDispatch, useSelector } from 'react-redux'
33
import { useEffect } from 'react'
4-
import { ConnectionString } from 'connection-string'
54
import { isNull, isNumber, every, values, pick, some } from 'lodash'
6-
import { Pages, REDIS_URI_SCHEMES } from 'uiSrc/constants'
5+
import { Pages } from 'uiSrc/constants'
76
import { ADD_NEW_CA_CERT, ADD_NEW } from 'uiSrc/pages/home/constants'
87
import {
98
appRedirectionSelector,
@@ -16,7 +15,7 @@ import { userSettingsSelector } from 'uiSrc/slices/user/user-settings'
1615
import { UrlHandlingActions } from 'uiSrc/slices/interfaces/urlHandling'
1716
import { autoCreateAndConnectToInstanceAction } from 'uiSrc/slices/instances/instances'
1817
import { getRedirectionPage } from 'uiSrc/utils/routing'
19-
import { Nullable, transformQueryParamsObject } from 'uiSrc/utils'
18+
import { Nullable, transformQueryParamsObject, parseRedisUrl } from 'uiSrc/utils'
2019

2120
const GlobalUrlHandler = () => {
2221
const { fromUrl } = useSelector(appRedirectionSelector)
@@ -60,7 +59,7 @@ const GlobalUrlHandler = () => {
6059
const from = params.get('from')
6160

6261
if (from) {
63-
dispatch(setFromUrl(decodeURIComponent(from)))
62+
dispatch(setFromUrl(from))
6463
history.replace({
6564
search: ''
6665
})
@@ -101,15 +100,14 @@ const GlobalUrlHandler = () => {
101100
)
102101
)
103102

104-
const url = new ConnectionString(redisUrl)
103+
const url = parseRedisUrl(redisUrl)
105104

106-
/* If a protocol exists, it should be a redis protocol */
107-
if (url.protocol && !REDIS_URI_SCHEMES.includes(url.protocol)) return
105+
if (!url) return
108106

109107
const obligatoryForAutoConnectFields = {
110-
host: url.hostname,
111-
port: url.port,
112-
username: url.user,
108+
host: url.host,
109+
port: url.port || 6379,
110+
username: url.username,
113111
password: url.password,
114112
}
115113

redisinsight/ui/src/pages/home/utils/form.tsx

Lines changed: 43 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { ConnectionString } from 'connection-string'
21
import { isUndefined, toString } from 'lodash'
32
import React from 'react'
43
import { FormikErrors } from 'formik'
5-
import { REDIS_URI_SCHEMES } from 'uiSrc/constants'
64
import { InstanceType } from 'uiSrc/slices/interfaces'
75
import {
86
ADD_NEW,
@@ -13,7 +11,7 @@ import {
1311
SshPassType
1412
} from 'uiSrc/pages/home/constants'
1513
import { DbConnectionInfo } from 'uiSrc/pages/home/interfaces'
16-
import { Nullable } from 'uiSrc/utils'
14+
import { Nullable, parseRedisUrl } from 'uiSrc/utils'
1715

1816
export const getTlsSettings = (values: DbConnectionInfo) => ({
1917
useTls: values.tls,
@@ -100,7 +98,7 @@ export const applySSHDatabase = (database: any, values: DbConnectionInfo) => {
10098
database.ssh = true
10199
database.sshOptions = {
102100
host: sshHost,
103-
port: +sshPort,
101+
port: sshPort ? +sshPort : undefined,
104102
username: sshUsername,
105103
}
106104

@@ -201,69 +199,54 @@ export const autoFillFormDetails = (
201199
instanceType: InstanceType
202200
): boolean => {
203201
try {
204-
const details = new ConnectionString(content)
202+
const details = parseRedisUrl(content)
205203

206-
/* If a protocol exists, it should be a redis protocol */
207-
if (details.protocol && !REDIS_URI_SCHEMES.includes(details.protocol)) return false
208-
/*
209-
* Auto fill logic:
210-
* 1) If the port is parsed, we are sure that the user has indeed copied a connection string.
211-
* '172.18.0.2:12000' => {host: '172,18.0.2', port: 12000}
212-
* 'redis-12000.cluster.local:12000' => {host: 'redis-12000.cluster.local', port: 12000}
213-
* 'lorem ipsum' => {host: undefined, port: undefined}
214-
* 2) If the port is `undefined` but a redis URI scheme is present as protocol, we follow
215-
* the "Scheme semantics" as mentioned in the official URI schemes.
216-
* i) redis:// - https://www.iana.org/assignments/uri-schemes/prov/redis
217-
* ii) rediss:// - https://www.iana.org/assignments/uri-schemes/prov/rediss
218-
*/
219-
if (
220-
details.port !== undefined
221-
|| REDIS_URI_SCHEMES.includes(details.protocol || '')
222-
) {
223-
const getUpdatedInitialValues = () => {
224-
switch (instanceType) {
225-
case InstanceType.RedisEnterpriseCluster: {
226-
return ({
227-
host: details.hostname || initialValues.host || 'localhost',
228-
port: `${details.port || initialValues.port || 9443}`,
229-
username: details.user || '',
230-
password: details.password || '',
231-
})
232-
}
204+
if (!details) return false
205+
206+
const getUpdatedInitialValues = () => {
207+
switch (instanceType) {
208+
case InstanceType.RedisEnterpriseCluster: {
209+
return ({
210+
host: details.host || initialValues.host || 'localhost',
211+
port: `${details.port || initialValues.port || 9443}`,
212+
username: details.username || '',
213+
password: details.password || '',
214+
})
215+
}
233216

234-
case InstanceType.Sentinel: {
235-
return ({
236-
host: details.hostname || initialValues.host || 'localhost',
237-
port: `${details.port || initialValues.port || 9443}`,
238-
username: details.user || '',
239-
password: details.password,
240-
tls: details.protocol === 'rediss',
241-
})
242-
}
217+
case InstanceType.Sentinel: {
218+
return ({
219+
host: details.host || initialValues.host || 'localhost',
220+
port: `${details.port || initialValues.port || 9443}`,
221+
username: details.username || '',
222+
password: details.password,
223+
tls: details.protocol === 'rediss',
224+
})
225+
}
243226

244-
case InstanceType.Standalone: {
245-
return ({
246-
name: details.host || initialValues.name || 'localhost:6379',
247-
host: details.hostname || initialValues.host || 'localhost',
248-
port: `${details.port || initialValues.port || 9443}`,
249-
username: details.user || '',
250-
password: details.password,
251-
tls: details.protocol === 'rediss',
252-
ssh: false,
253-
sshPassType: SshPassType.Password
254-
})
255-
}
256-
default: {
257-
return {}
258-
}
227+
case InstanceType.Standalone: {
228+
return ({
229+
name: details.host || initialValues.name || 'localhost:6379',
230+
host: details.host || initialValues.host || 'localhost',
231+
port: `${details.port || initialValues.port || 9443}`,
232+
username: details.username || '',
233+
password: details.password,
234+
tls: details.protocol === 'rediss',
235+
db: details.dbNumber,
236+
ssh: false,
237+
sshPassType: SshPassType.Password
238+
})
239+
}
240+
default: {
241+
return {}
259242
}
260243
}
261-
setInitialValues(getFormValues(getUpdatedInitialValues()))
262-
/*
244+
}
245+
setInitialValues(getFormValues(getUpdatedInitialValues()))
246+
/*
263247
* autofill was successfull so return true
264248
*/
265-
return true
266-
}
249+
return true
267250
} catch (err) {
268251
/* The pasted content is not a connection URI so ignore. */
269252
return false

redisinsight/ui/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export * from './statuses'
88
export * from './instance'
99
export * from './apiResponse'
1010
export * from './parseResponse'
11+
export * from './parseRedisUrl'
1112
export * from './comparisons'
1213
export * from './longNames'
1314
export * from './cliHelper'
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Maybe, Nullable } from 'uiSrc/utils/types'
2+
3+
/*
4+
redis[s]:// - Protocol (redis or rediss)
5+
[username][:password]@ - Optional username and password
6+
host - Hostname or IP address
7+
[:port] - Optional port
8+
[/db-number] - Optional database number
9+
*/
10+
11+
interface ParsedRedisUrl {
12+
protocol: string
13+
username: string
14+
password: string
15+
host: string
16+
port: Maybe<number>
17+
dbNumber: Maybe<number>
18+
}
19+
20+
const parseRedisUrl = (urlString: string): Nullable<ParsedRedisUrl> => {
21+
// eslint-disable-next-line no-useless-escape
22+
const redisUrlPattern = /^(redis[s]?):\/\/(?:([^:@]+)?(?::([^@]+))?@)?([^:\/]+)(?::(\d+))?(?:\/(\d+))?$/
23+
const match = urlString.match(redisUrlPattern)
24+
25+
if (!match) {
26+
return null
27+
}
28+
29+
const [, protocol, username, password, host, port, dbNumber] = match
30+
31+
return {
32+
protocol,
33+
username: username || '',
34+
password: password || '',
35+
host,
36+
port: port ? parseInt(port, 10) : undefined,
37+
dbNumber: dbNumber ? parseInt(dbNumber, 10) : undefined
38+
}
39+
}
40+
41+
export {
42+
parseRedisUrl
43+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { parseRedisUrl } from 'uiSrc/utils/parseRedisUrl'
2+
3+
const defaultRedisParams = {
4+
username: '',
5+
password: '',
6+
port: undefined,
7+
dbNumber: undefined
8+
}
9+
10+
const parseRedisUrlTests: Array<[string, any]> = [
11+
[
12+
'http://user:pass@localhost:6380',
13+
null
14+
],
15+
[
16+
'redis://:@localhost:6380',
17+
null
18+
],
19+
[
20+
'redis://us@er:pass@localhost:6380',
21+
null
22+
],
23+
[
24+
'redis://localhost',
25+
{ ...defaultRedisParams, protocol: 'redis', host: 'localhost' }
26+
],
27+
[
28+
'redis://localhost:6380',
29+
{ ...defaultRedisParams, protocol: 'redis', host: 'localhost', port: 6380 }
30+
],
31+
[
32+
'redis://@localhost:6380',
33+
{ ...defaultRedisParams, protocol: 'redis', host: 'localhost', port: 6380 }
34+
],
35+
[
36+
'redis://user@localhost:6380',
37+
{ ...defaultRedisParams, username: 'user', protocol: 'redis', host: 'localhost', port: 6380 }
38+
],
39+
[
40+
'redis://:pass@localhost:6380',
41+
{ ...defaultRedisParams, protocol: 'redis', password: 'pass', host: 'localhost', port: 6380 }
42+
],
43+
[
44+
'redis://user:pass@localhost:6380',
45+
{ ...defaultRedisParams, protocol: 'redis', username: 'user', password: 'pass', host: 'localhost', port: 6380 }
46+
],
47+
[
48+
'rediss://user:pa%712ss@localhost:6380',
49+
{ ...defaultRedisParams, protocol: 'rediss', username: 'user', password: 'pa%712ss', host: 'localhost', port: 6380 }
50+
],
51+
[
52+
'rediss://d&&21^$:pa%[email protected]:6380',
53+
{ ...defaultRedisParams, protocol: 'rediss', username: 'd&&21^$', password: 'pa%712ss', host: 'local-host-123.net.com', port: 6380 }
54+
],
55+
[
56+
'rediss://user:pa%712ss@localhost:6380/2',
57+
{ protocol: 'rediss', username: 'user', password: 'pa%712ss', host: 'localhost', port: 6380, dbNumber: 2 }
58+
],
59+
]
60+
61+
describe('parseRedisUrl', () => {
62+
it.each(parseRedisUrlTests)('for input: %s (index), %s (shift), should be output: %s',
63+
(url, expected) => {
64+
const result = parseRedisUrl(url)
65+
expect(result).toEqual(expected)
66+
})
67+
})

0 commit comments

Comments
 (0)