Skip to content

Commit c937745

Browse files
authored
Merge pull request neo4j#790 from oskarhane/btoa-fix
Fix issue with creating unique keys from user input
2 parents 3a399f7 + 08c3a23 commit c937745

File tree

7 files changed

+159
-19
lines changed

7 files changed

+159
-19
lines changed

src/browser/components/Tables.jsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
* You should have received a copy of the GNU General Public License
1818
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1919
*/
20-
/* global btoa */
2120
import React from 'react'
2221
import styled from 'styled-components'
22+
import { toKeyString } from 'services/utils'
2323

2424
const StyledTable = styled.table`
2525
border-radius: 4px;
@@ -42,8 +42,12 @@ const StyledTh = styled.th`
4242
border-color: #ddd;
4343
padding: 10px 15px;
4444
`
45-
const StyledTd = styled.td`padding: 5px;`
46-
const StyledTdKey = styled(StyledTd)`font-weight: bold;`
45+
const StyledTd = styled.td`
46+
padding: 5px;
47+
`
48+
const StyledTdKey = styled(StyledTd)`
49+
font-weight: bold;
50+
`
4751
export const SysInfoTableContainer = styled.div`
4852
display: flex;
4953
flex-flow: row wrap;
@@ -80,7 +84,7 @@ export const SysInfoTableEntry = ({
8084
const mappedValue = getValue(value, mapper)
8185
const val = mappedValue || missingValuePlaceholder
8286
return mappedValue || !optional ? (
83-
<StyledTdKey key={btoa(val)}>{val}</StyledTdKey>
87+
<StyledTdKey key={toKeyString(val)}>{val}</StyledTdKey>
8488
) : null
8589
})}
8690
</StyledTr>
@@ -93,7 +97,7 @@ export const SysInfoTableEntry = ({
9397
const mappedValue = getValue(value, mapper)
9498
const val = mappedValue || missingValuePlaceholder
9599
return mappedValue || !optional ? (
96-
<StyledTd key={btoa(val)}>{val}</StyledTd>
100+
<StyledTd key={toKeyString(val)}>{val}</StyledTd>
97101
) : null
98102
})}
99103
</StyledTr>

src/browser/modules/Sidebar/Favorites.jsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1919
*/
2020

21-
/* global btoa */
2221
import React, { Component } from 'react'
2322
import { connect } from 'react-redux'
2423
import { withBus } from 'react-suber'
@@ -27,6 +26,7 @@ import * as favorite from 'shared/modules/favorites/favoritesDuck'
2726
import * as folder from 'shared/modules/favorites/foldersDuck'
2827
import { getSettings } from 'shared/modules/settings/settingsDuck'
2928
import { executeCommand } from 'shared/modules/commands/commandsDuck'
29+
import { toKeyString } from 'services/utils'
3030

3131
import Render from 'browser-components/Render'
3232
import Favorite from './Favorite'
@@ -48,7 +48,7 @@ const mapFavorites = (favorites, props, isChild, moveAction) => {
4848
return (
4949
<Favorite
5050
entry={entry}
51-
key={`${btoa(entry.name + entry.content + entry.id)}`}
51+
key={`${toKeyString(entry.name + entry.content + entry.id)}`}
5252
id={entry.id}
5353
name={entry.name}
5454
content={entry.content}
@@ -257,5 +257,10 @@ const mapDispatchToProps = (dispatch, ownProps) => {
257257
}
258258
}
259259
export default DragDropContext(HTML5Backend)(
260-
withBus(connect(mapStateToProps, mapDispatchToProps)(Favorites))
260+
withBus(
261+
connect(
262+
mapStateToProps,
263+
mapDispatchToProps
264+
)(Favorites)
265+
)
261266
)

src/browser/modules/Sidebar/Settings.jsx

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
* You should have received a copy of the GNU General Public License
1818
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1919
*/
20-
/* global btoa */
2120
import React from 'react'
2221
import { connect } from 'react-redux'
2322
import * as actions from 'shared/modules/settings/settingsDuck'
@@ -35,6 +34,7 @@ import {
3534
StyledSettingLabel,
3635
StyledSettingTextInput
3736
} from './styled'
37+
import { toKeyString } from 'services/utils'
3838

3939
const visualSettings = [
4040
{
@@ -140,20 +140,24 @@ const visualSettings = [
140140
}
141141
]
142142

143-
export const Settings = ({ settings, onSettingsSave = () => {} }) => {
143+
export const Settings = ({
144+
settings,
145+
visualSettings,
146+
onSettingsSave = () => {}
147+
}) => {
144148
if (!settings) return null
145-
const mappedSettings = visualSettings.map((visualSetting, oi) => {
149+
const mappedSettings = visualSettings.map(visualSetting => {
146150
const title = <DrawerSubHeader>{visualSetting.title}</DrawerSubHeader>
147151
const mapSettings = visualSetting.settings
148-
.map((settingObj, i) => {
152+
.map(settingObj => {
149153
const setting = Object.keys(settingObj)[0]
150154
if (typeof settings[setting] === 'undefined') return false
151155
const visual = settingObj[setting].displayName
152156
const tooltip = settingObj[setting].tooltip || ''
153157

154158
if (!settingObj[setting].type || settingObj[setting].type === 'input') {
155159
return (
156-
<StyledSetting key={btoa(visual)}>
160+
<StyledSetting key={toKeyString(visual)}>
157161
<StyledSettingLabel title={tooltip}>{visual}</StyledSettingLabel>
158162
<StyledSettingTextInput
159163
onChange={event => {
@@ -168,7 +172,7 @@ export const Settings = ({ settings, onSettingsSave = () => {} }) => {
168172
)
169173
} else if (settingObj[setting].type === 'radio') {
170174
return (
171-
<StyledSetting key={btoa(visual)}>
175+
<StyledSetting key={toKeyString(visual)}>
172176
<StyledSettingLabel title={tooltip}>{visual}</StyledSettingLabel>
173177
<RadioSelector
174178
options={settingObj[setting].options}
@@ -182,7 +186,7 @@ export const Settings = ({ settings, onSettingsSave = () => {} }) => {
182186
)
183187
} else if (settingObj[setting].type === 'checkbox') {
184188
return (
185-
<StyledSetting key={btoa(visual)}>
189+
<StyledSetting key={toKeyString(visual)}>
186190
<CheckboxSelector
187191
onChange={event => {
188192
settings[setting] = event.target.checked
@@ -197,7 +201,7 @@ export const Settings = ({ settings, onSettingsSave = () => {} }) => {
197201
})
198202
.filter(setting => setting !== false)
199203
return (
200-
<React.Fragment key={btoa(visualSetting.title)}>
204+
<React.Fragment key={toKeyString(visualSetting.title)}>
201205
{title}
202206
{mapSettings}
203207
</React.Fragment>
@@ -218,7 +222,8 @@ export const Settings = ({ settings, onSettingsSave = () => {} }) => {
218222

219223
const mapStateToProps = state => {
220224
return {
221-
settings: state.settings
225+
settings: state.settings,
226+
visualSettings
222227
}
223228
}
224229

@@ -230,4 +235,7 @@ const mapDispatchToProps = dispatch => {
230235
}
231236
}
232237

233-
export default connect(mapStateToProps, mapDispatchToProps)(Settings)
238+
export default connect(
239+
mapStateToProps,
240+
mapDispatchToProps
241+
)(Settings)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2002-2018 "Neo4j, Inc"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Neo4j is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
/* global describe, beforeEach, afterEach, test, expect */
22+
import React from 'react'
23+
import { render, cleanup } from 'react-testing-library'
24+
import { Settings } from './Settings'
25+
26+
afterEach(cleanup)
27+
28+
test('Settings renders with strange characters in display name', () => {
29+
// Given
30+
const settings = { testSetting: true }
31+
const visualSettings = [
32+
{
33+
title: 'Test åäö settings',
34+
settings: [
35+
{
36+
testSetting: {
37+
displayName: 'åäö üüü'
38+
}
39+
}
40+
]
41+
}
42+
]
43+
44+
// When
45+
const { container } = render(
46+
<Settings settings={settings} visualSettings={visualSettings} />
47+
)
48+
49+
// Then
50+
expect(container).toMatchSnapshot()
51+
})
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Settings renders with strange characters in display name 1`] = `
4+
<div>
5+
<div
6+
class="sc-bdVaJa fFGtIw"
7+
id="db-settings"
8+
>
9+
<h4
10+
class="sc-bwzfXH gJZKwg"
11+
>
12+
Browser Settings
13+
</h4>
14+
<div
15+
class="sc-bZQynM hYejcx"
16+
>
17+
<div
18+
class="sc-ifAKCX jDKYZc"
19+
>
20+
<div
21+
class="sc-EHOje fyzZhj"
22+
>
23+
<h5
24+
class="sc-bxivhb jqgGlu"
25+
>
26+
Test åäö settings
27+
</h5>
28+
<div
29+
class="sc-VigVT kULqyn"
30+
>
31+
<div
32+
class="sc-jTzLTM cjMpzP"
33+
title=""
34+
>
35+
åäö üüü
36+
</div>
37+
<input
38+
class="testSetting sc-fjdhpX hFlOzk"
39+
title=""
40+
value="true"
41+
/>
42+
</div>
43+
</div>
44+
</div>
45+
</div>
46+
</div>
47+
</div>
48+
`;

src/shared/services/utils.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
* You should have received a copy of the GNU General Public License
1818
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1919
*/
20+
/* global btoa */
21+
2022
import parseUrl from 'url-parse'
2123

2224
export const deepEquals = (x, y) => {
@@ -258,7 +260,9 @@ export const stringifyMod = (
258260
) => {
259261
prettyLevel = !prettyLevel
260262
? false
261-
: prettyLevel === true ? 1 : parseInt(prettyLevel)
263+
: prettyLevel === true
264+
? 1
265+
: parseInt(prettyLevel)
262266
const nextPrettyLevel = prettyLevel ? prettyLevel + 1 : false
263267
const newLine = prettyLevel ? '\n' : ''
264268
const indentation =
@@ -395,3 +399,5 @@ export const optionalToString = v =>
395399
![null, undefined].includes(v) && typeof v.toString === 'function'
396400
? v.toString()
397401
: v
402+
403+
export const toKeyString = str => btoa(encodeURIComponent(str))

src/shared/services/utils.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,3 +520,21 @@ describe('Object props manipulation', () => {
520520
})
521521
})
522522
})
523+
describe('toKeyString', () => {
524+
it('can encode strings with special characters', () => {
525+
// Given
526+
const strs = [
527+
{ str: 'hey ho ', expect: 'aGV5JTIwaG8lMjAlRUYlQTMlQkY=' },
528+
{
529+
str: '✓ à la mode',
530+
expect: 'JUUyJTlDJTkzJTIwJUMzJUEwJTIwbGElMjBtb2Rl'
531+
},
532+
{ str: '😍', expect: 'JUYwJTlGJTk4JThE' }
533+
]
534+
535+
// When & Then
536+
strs.forEach(str => {
537+
expect(utils.toKeyString(str.str)).toEqual(str.expect)
538+
})
539+
})
540+
})

0 commit comments

Comments
 (0)