Skip to content

Commit 5986c70

Browse files
authored
feat: use secrets in notebook (#3624)
* 2539: create editor context, used by any monaco editor. handles injection based on enum option for sameLine or nextLine inserts. * 2539: restructure how function injects work in notebooks, to utilize the new editor context. And create shared DRY components. * 2539: injection of secrets into flow's rawFlux pipe. * fix: debug flaky userAccount test:
1 parent 0606431 commit 5986c70

File tree

25 files changed

+698
-478
lines changed

25 files changed

+698
-478
lines changed

cypress/e2e/cloud/userAccounts.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,12 @@ describe('Account Page; user with two accounts', () => {
113113

114114
cy.getByTestID('input--active-account-name').should('have.value', 'Influx')
115115

116+
cy.getByTestID('input--active-account-name').clear()
117+
cy.getByTestID('input--active-account-name').should('have.value', '')
118+
116119
// what can I say? i am a fan
117120
const newName = 'Bruno-no-no-no'
118-
119-
cy.getByTestID('input--active-account-name')
120-
.clear()
121-
.type(newName)
121+
cy.getByTestID('input--active-account-name').type(newName)
122122
cy.getByTestID('rename-account--button').click()
123123

124124
// test that the notification is up:

src/flows/components/Sidebar.scss

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
text-align: right;
6363
}
6464

65-
.flow-sidebar--client-list {
65+
.flow-sidebar--client-list,
66+
.flow-sidebar--secrets-list {
6667
margin-right: -16px;
6768
}

src/flows/components/Sidebar.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
import React, {FC, useEffect, useContext, useRef} from 'react'
2+
import {useDispatch} from 'react-redux'
3+
4+
// Components
25
import {
36
Button,
47
ComponentColor,
58
IconFont,
69
DapperScrollbars,
710
Dropdown,
811
} from '@influxdata/clockface'
9-
import {useDispatch} from 'react-redux'
12+
import {ControlSection, ControlAction, Submenu} from 'src/types/flows'
13+
import ClientList from 'src/flows/components/ClientList'
1014

11-
// Contexts
15+
// Context
1216
import {FlowContext} from 'src/flows/context/flow.current'
1317
import {FlowQueryContext} from 'src/flows/context/flow.query'
1418
import {SidebarContext} from 'src/flows/context/sidebar'
1519

16-
// Components
17-
import {ControlSection, ControlAction, Submenu} from 'src/types/flows'
18-
import ClientList from 'src/flows/components/ClientList'
19-
import './Sidebar.scss'
20-
21-
// Utils
20+
// Utils & Actions
2221
import {event} from 'src/cloud/utils/reporting'
2322
import {notify} from 'src/shared/actions/notifications'
2423

@@ -29,6 +28,8 @@ import {
2928
} from 'src/shared/copy/notifications'
3029
import {PIPE_DEFINITIONS} from 'src/flows'
3130

31+
import './Sidebar.scss'
32+
3233
export const SubSideBar: FC = () => {
3334
const {flow} = useContext(FlowContext)
3435
const {submenu, hideSub} = useContext(SidebarContext)

src/flows/context/editor.tsx

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import React, {
2+
FC,
3+
createContext,
4+
useState,
5+
useCallback,
6+
useContext,
7+
} from 'react'
8+
import {EditorType} from 'src/types'
9+
import {PipeContext} from 'src/flows/context/pipe'
10+
11+
export enum InjectionType {
12+
OnOwnLine = 'onOwnLine',
13+
SameLine = 'sameLine',
14+
}
15+
16+
interface InjectionPosition {
17+
row: number
18+
column: number
19+
shouldStartWithNewLine: boolean
20+
shouldEndInNewLine: boolean
21+
}
22+
23+
export interface InjectionOptions {
24+
header?: string | null
25+
text: string
26+
type: InjectionType
27+
}
28+
29+
export interface EditorContextType {
30+
editor: EditorType | null
31+
setEditor: (editor: EditorType) => void
32+
inject: (options: InjectionOptions) => void
33+
calcInjectiontPosition: (type: InjectionType) => Partial<InjectionPosition>
34+
updateText: (t: string) => void
35+
}
36+
37+
const DEFAULT_CONTEXT: EditorContextType = {
38+
editor: null,
39+
setEditor: _ => {},
40+
inject: _ => {},
41+
calcInjectiontPosition: _ => ({}),
42+
updateText: _ => {},
43+
}
44+
45+
export const EditorContext = createContext<EditorContextType>(DEFAULT_CONTEXT)
46+
47+
export const EditorProvider: FC = ({children}) => {
48+
const [editor, setEditor] = useState<EditorType>(null)
49+
const {data, update} = useContext(PipeContext)
50+
const {queries, activeQuery} = data
51+
52+
const updateText = useCallback(
53+
text => {
54+
const _queries = [...queries]
55+
_queries[activeQuery] = {
56+
...queries[activeQuery],
57+
text,
58+
}
59+
60+
update({queries: _queries})
61+
},
62+
[queries, activeQuery]
63+
)
64+
65+
const calcInjectiontPosition = useCallback(
66+
(type: InjectionType): Partial<InjectionPosition> => {
67+
if (!editor) {
68+
return {}
69+
}
70+
const {lineNumber, column: col} = editor.getPosition()
71+
let row = lineNumber
72+
let column = col
73+
74+
const queryText = editor.getModel().getValue()
75+
const split = queryText.split('\n')
76+
const getCurrentLineText = () => {
77+
// row is not zero indexed in monaco editor. 1..N
78+
return (split[row - 1] || split[split.length - 1]).trimEnd()
79+
}
80+
81+
let currentLineText = getCurrentLineText()
82+
// column is not zero indexed in monnao editor. 1..N
83+
let textAheadOfCursor = currentLineText.slice(0, column - 1).trim()
84+
let textBehindCursor = currentLineText.slice(column - 1).trim()
85+
86+
// if cursor has text in front of it, and should be onOwnRow
87+
if (type == InjectionType.OnOwnLine && textAheadOfCursor) {
88+
row++
89+
column = 1
90+
}
91+
// edge case for when user toggles to the script editor
92+
// this defaults the cursor to the initial position (top-left, 1:1 position)
93+
const [currentRange] = editor.getVisibleRanges()
94+
if (row === 1 && column === 1) {
95+
row = currentRange.endLineNumber + 1
96+
}
97+
98+
if (row !== lineNumber) {
99+
currentLineText = getCurrentLineText()
100+
textAheadOfCursor = currentLineText.slice(0, column - 1).trim()
101+
textBehindCursor = currentLineText.slice(column - 1).trim()
102+
}
103+
104+
let shouldEndInNewLine = false
105+
// if cursor has text behind it, and should be onOwnRow
106+
if (type == InjectionType.OnOwnLine && textBehindCursor) {
107+
shouldEndInNewLine = true
108+
}
109+
110+
const cursorInMiddleOfText = textAheadOfCursor && textBehindCursor
111+
if (type == InjectionType.OnOwnLine && cursorInMiddleOfText) {
112+
row++
113+
column = 1
114+
shouldEndInNewLine = true
115+
}
116+
117+
// if we asked to insert on a row out-of-range
118+
// then need to manually append newline to front of row
119+
const shouldStartWithNewLine = row > currentRange.endLineNumber
120+
121+
return {row, column, shouldStartWithNewLine, shouldEndInNewLine}
122+
},
123+
[editor]
124+
)
125+
126+
const inject = useCallback(
127+
(options: InjectionOptions) => {
128+
if (!editor) {
129+
return {}
130+
}
131+
132+
const {header, text: initT, type} = options
133+
const {
134+
row,
135+
column: initC,
136+
shouldStartWithNewLine,
137+
shouldEndInNewLine,
138+
} = calcInjectiontPosition(type)
139+
let text = ''
140+
141+
if (shouldStartWithNewLine) {
142+
text = `\n${initT}`
143+
} else {
144+
text = initT
145+
}
146+
147+
if (shouldEndInNewLine) {
148+
text = `${text}\n`
149+
}
150+
151+
const column = type == InjectionType.OnOwnLine ? 1 : initC
152+
const range = new window.monaco.Range(row, column, row, column)
153+
const edits = [
154+
{
155+
range,
156+
text,
157+
},
158+
]
159+
160+
if (
161+
header &&
162+
!editor
163+
.getModel()
164+
.getValue()
165+
.includes(header)
166+
) {
167+
edits.unshift({
168+
range: new window.monaco.Range(1, 1, 1, 1),
169+
text: `${header}\n`,
170+
})
171+
}
172+
173+
editor.executeEdits('', edits)
174+
updateText(editor.getValue())
175+
},
176+
[editor, calcInjectiontPosition, updateText]
177+
)
178+
179+
return (
180+
<EditorContext.Provider
181+
value={{
182+
editor,
183+
setEditor,
184+
inject,
185+
calcInjectiontPosition,
186+
updateText,
187+
}}
188+
>
189+
{children}
190+
</EditorContext.Provider>
191+
)
192+
}

src/flows/pipes/Notification/view.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ import 'src/flows/pipes/Notification/styles.scss'
6969

7070
// Constants
7171
import {UNPROCESSED_PANEL_TEXT} from 'src/flows'
72-
import CreateSecretForm from 'src/secrets/components/CreateSecretForm'
72+
import CreateSecretForm from 'src/secrets/components/CreateSecret/CreateSecretForm'
7373

7474
const Notification: FC<PipeProp> = ({Context}) => {
7575
const dispatch = useDispatch()
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Libraries
2+
import React, {FC, createRef} from 'react'
3+
4+
// Component
5+
import {
6+
Popover,
7+
PopoverPosition,
8+
PopoverInteraction,
9+
Appearance,
10+
Button,
11+
ComponentSize,
12+
ComponentColor,
13+
} from '@influxdata/clockface'
14+
15+
type OptionType = Record<string, any>
16+
17+
interface Props {
18+
option: OptionType
19+
extractor: (func: OptionType) => string
20+
onClick: (func: OptionType) => void
21+
testID: string
22+
ToolTipContent?: (props: {item: any}) => JSX.Element
23+
}
24+
25+
const defaultProps = {
26+
testID: 'flux-injection-option',
27+
}
28+
29+
const FluxInjectionOption: FC<Props> = ({
30+
option,
31+
extractor,
32+
onClick,
33+
testID,
34+
ToolTipContent,
35+
}) => {
36+
const itemRef = createRef<HTMLDListElement>()
37+
const handleClick = () => {
38+
onClick(option)
39+
}
40+
41+
return (
42+
<>
43+
{!!ToolTipContent && (
44+
<Popover
45+
appearance={Appearance.Outline}
46+
enableDefaultStyles={false}
47+
position={PopoverPosition.ToTheLeft}
48+
triggerRef={itemRef}
49+
showEvent={PopoverInteraction.Hover}
50+
hideEvent={PopoverInteraction.Hover}
51+
distanceFromTrigger={8}
52+
testID="toolbar-popover"
53+
contents={() => <ToolTipContent item={option} />}
54+
/>
55+
)}
56+
<dd
57+
ref={itemRef}
58+
data-testid={`flux--${testID}`}
59+
className="flux-toolbar--list-item flux-toolbar--function"
60+
>
61+
<code>{extractor(option)}</code>
62+
<Button
63+
testID={`flux--${testID}--inject`}
64+
text="Inject"
65+
onClick={handleClick}
66+
size={ComponentSize.ExtraSmall}
67+
className="flux-toolbar--injector"
68+
color={ComponentColor.Primary}
69+
/>
70+
</dd>
71+
</>
72+
)
73+
}
74+
75+
FluxInjectionOption.defaultProps = defaultProps
76+
77+
export default FluxInjectionOption

0 commit comments

Comments
 (0)