Skip to content

Commit 5254811

Browse files
authored
feat: list flux functions from fluxdocsd (#4058)
* feat: fetch flux packages from fluxdocsd * fix: prettier * fix: prettier * chore: clean up * feat: add flux docs to redux store * fix: prettier * fix: prettier * chore: clean up * feat: memoize function search * chore: cleanup * chore: clean up * feat: remove action type from thunks * chore: disable hook/exhaustive-deps rule * feat: add useMemo to sorted func * chore: clean up
1 parent 86f93d8 commit 5254811

File tree

12 files changed

+213
-82
lines changed

12 files changed

+213
-82
lines changed

src/shared/copy/notifications/categories/alerts.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,8 @@ export const getResourcesTokensFailure = (
132132
...defaultErrorNotification,
133133
message: `Failed to fetch all resources for creating ${tokenType}`,
134134
})
135+
136+
export const getFluxPackagesFailed = (message: string): Notification => ({
137+
...defaultErrorNotification,
138+
message: `Failed to fetch flux functions: ${message}`,
139+
})

src/store/configureStore.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import flagReducer from 'src/shared/reducers/flags'
2222
import currentDashboardReducer from 'src/shared/reducers/currentDashboard'
2323
import currentExplorerReducer from 'src/shared/reducers/currentExplorer'
2424
import currentPageReducer from 'src/shared/reducers/currentPage'
25+
import fluxDocsReducer from 'src/timeMachine/reducers/fluxDocsReducer'
2526
import tasksReducer from 'src/tasks/reducers'
2627
import rangesReducer from 'src/dashboards/reducers/ranges'
2728
import {dashboardsReducer} from 'src/dashboards/reducers/dashboards'
@@ -83,6 +84,7 @@ export const rootReducer = (history: History) => (state, action) => {
8384
currentPage: currentPageReducer,
8485
currentDashboard: currentDashboardReducer,
8586
currentExplorer: currentExplorerReducer,
87+
fluxDocs: fluxDocsReducer,
8688
dataLoading: dataLoadingReducer,
8789
me: meReducer,
8890
flags: flagReducer,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Libraries
2+
import {Dispatch} from 'react'
3+
import {get} from 'lodash'
4+
5+
// API
6+
import {FluxdocsArray, getFluxdocs} from 'src/client/fluxdocsdRoutes'
7+
8+
// Actions
9+
import {getFluxPackagesFailed} from 'src/shared/copy/notifications/categories/alerts'
10+
import {notify} from 'src/shared/actions/notifications'
11+
12+
// Types
13+
import {NotificationAction} from 'src/types'
14+
15+
export const GET_FLUX_DOCS = 'GET_FLUX_DOCS'
16+
17+
export type Action = ReturnType<typeof setFluxFunc>
18+
19+
export const setFluxFunc = (data: FluxdocsArray) => ({
20+
type: 'GET_FLUX_DOCS',
21+
payload: {
22+
data,
23+
},
24+
})
25+
26+
export const getFluxPackages = () => async (
27+
dispatch: Dispatch<Action | NotificationAction>
28+
) => {
29+
try {
30+
const resp = await getFluxdocs({})
31+
32+
if (resp.status !== 200) {
33+
throw new Error(resp.data.message)
34+
}
35+
36+
dispatch(setFluxFunc(resp.data))
37+
} catch (error) {
38+
console.error(error)
39+
const message = get(error, 'response.data.message', '')
40+
dispatch(notify(getFluxPackagesFailed(message)))
41+
}
42+
}
Lines changed: 86 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,115 @@
11
// Libraries
2-
import React, {FC, useMemo, useState, useCallback} from 'react'
2+
import React, {FC, useCallback, useEffect, useMemo, useState} from 'react'
3+
import {connect, ConnectedProps} from 'react-redux'
34

45
// Components
5-
import TransformToolbarFunctions from 'src/timeMachine/components/dynamicFluxFunctionsToolbar/TransformToolbarFunctions'
6-
import FluxToolbarSearch from 'src/timeMachine/components/FluxToolbarSearch'
76
import {DapperScrollbars} from '@influxdata/clockface'
87
import ErrorBoundary from 'src/shared/components/ErrorBoundary'
8+
import FluxToolbarSearch from 'src/timeMachine/components/FluxToolbarSearch'
9+
import {SpinnerContainer, TechnoSpinner} from '@influxdata/clockface'
10+
import TransformToolbarFunctions from 'src/timeMachine/components/dynamicFluxFunctionsToolbar/TransformToolbarFunctions'
911
import ToolbarFunction from 'src/timeMachine/components/dynamicFluxFunctionsToolbar/ToolbarFunction'
1012

11-
// Constants
12-
import {FLUX_FUNCTIONS} from 'src/shared/constants/fluxFunctions'
13+
// Actions
14+
import {getFluxPackages} from 'src/timeMachine/actions/scriptEditorThunks'
1315

1416
// Types
15-
import {FluxToolbarFunction} from 'src/types'
17+
import {AppState} from 'src/types'
18+
import {Fluxdocs} from 'src/client/fluxdocsdRoutes'
19+
import {RemoteDataState} from 'src/types'
1620

1721
interface OwnProps {
18-
onInsertFluxFunction: (func: FluxToolbarFunction) => void
22+
onInsertFluxFunction: (func) => void
1923
}
2024

21-
const DynamicFluxFunctionsToolbar: FC<OwnProps> = (props: OwnProps) => {
25+
interface DispatchProps {
26+
getFluxPackages: () => void
27+
}
28+
29+
type ReduxProps = ConnectedProps<typeof connector>
30+
type Props = ReduxProps & OwnProps & DispatchProps
31+
32+
const DynamicFluxFunctionsToolbar: FC<Props> = (props: Props) => {
2233
const [searchTerm, setSearchTerm] = useState('')
34+
const [fluxLoadingState, setFluxLoadingState] = useState<RemoteDataState>(
35+
RemoteDataState.NotStarted
36+
)
37+
38+
useEffect(() => {
39+
const getFluxFuncs = async () => {
40+
try {
41+
setFluxLoadingState(RemoteDataState.Loading)
42+
43+
if (fluxFunctions.length === 0) {
44+
await getFluxPackages()
45+
setFluxLoadingState(RemoteDataState.Done)
46+
}
47+
setFluxLoadingState(RemoteDataState.Done)
48+
} catch (err) {
49+
console.error(err)
50+
setFluxLoadingState(RemoteDataState.Error)
51+
}
52+
}
53+
getFluxFuncs()
54+
}, []) // eslint-disable-line react-hooks/exhaustive-deps
55+
56+
const {onInsertFluxFunction, fluxFunctions, getFluxPackages} = props
2357

2458
const handleSearch = (searchTerm: string): void => {
2559
setSearchTerm(searchTerm)
2660
}
2761

2862
const handleClickFunction = useCallback(
29-
(func: FluxToolbarFunction) => {
30-
props.onInsertFluxFunction(func)
63+
(func: Fluxdocs) => {
64+
onInsertFluxFunction(func)
3165
},
32-
[props.onInsertFluxFunction]
66+
[onInsertFluxFunction]
3367
)
3468

3569
return useMemo(() => {
3670
return (
37-
<ErrorBoundary>
38-
<FluxToolbarSearch onSearch={handleSearch} resourceName="Functions" />
39-
<DapperScrollbars className="flux-toolbar--scroll-area">
40-
<div className="flux-toolbar--list" data-testid="flux-toolbar--list">
41-
<TransformToolbarFunctions
42-
funcs={FLUX_FUNCTIONS}
43-
searchTerm={searchTerm}
71+
<SpinnerContainer
72+
loading={fluxLoadingState}
73+
spinnerComponent={<TechnoSpinner />}
74+
>
75+
<ErrorBoundary>
76+
<FluxToolbarSearch onSearch={handleSearch} resourceName="Functions" />
77+
<DapperScrollbars className="flux-toolbar--scroll-area">
78+
<div
79+
className="flux-toolbar--list"
80+
data-testid="flux-toolbar--list"
4481
>
45-
{sortedFunctions =>
46-
sortedFunctions.map(func => (
47-
<ToolbarFunction
48-
onClickFunction={handleClickFunction}
49-
key={`${func.name}_${func.desc}`}
50-
func={func}
51-
testID={func.name}
52-
/>
53-
))
54-
}
55-
</TransformToolbarFunctions>
56-
</div>
57-
</DapperScrollbars>
58-
</ErrorBoundary>
82+
<TransformToolbarFunctions
83+
funcs={fluxFunctions}
84+
searchTerm={searchTerm}
85+
>
86+
{sortedFunctions =>
87+
sortedFunctions.map((func, index) => (
88+
<ToolbarFunction
89+
onClickFunction={handleClickFunction}
90+
key={index}
91+
func={func}
92+
testID={func.name}
93+
/>
94+
))
95+
}
96+
</TransformToolbarFunctions>
97+
</div>
98+
</DapperScrollbars>
99+
</ErrorBoundary>
100+
</SpinnerContainer>
59101
)
60-
}, [searchTerm, handleClickFunction])
102+
}, [fluxFunctions, fluxLoadingState, handleClickFunction, searchTerm])
103+
}
104+
105+
const mstp = (state: AppState) => {
106+
const fluxFunctions = state.fluxDocs.fluxDocs
107+
return {fluxFunctions}
108+
}
109+
110+
const mdtp = {
111+
getFluxPackages,
61112
}
113+
const connector = connect(mstp, mdtp)
62114

63-
export default DynamicFluxFunctionsToolbar
115+
export default connector(DynamicFluxFunctionsToolbar)

src/timeMachine/components/dynamicFluxFunctionsToolbar/FunctionTooltipContents.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,29 @@ import React, {FunctionComponent} from 'react'
33

44
// Components
55
import {DapperScrollbars} from '@influxdata/clockface'
6-
import TooltipDescription from 'src/timeMachine/components/dynamicFluxFunctionsToolbar/TooltipDescription'
76
import TooltipArguments from 'src/timeMachine/components/dynamicFluxFunctionsToolbar/TooltipArguments'
7+
import TooltipDescription from 'src/timeMachine/components/dynamicFluxFunctionsToolbar/TooltipDescription'
88
import TooltipLink from 'src/timeMachine/components/dynamicFluxFunctionsToolbar/TooltipLink'
99

1010
// Types
11-
import {FluxToolbarFunction} from 'src/types/shared'
12-
11+
import {Fluxdocs} from 'src/client/fluxdocsdRoutes'
1312
interface Props {
14-
func: FluxToolbarFunction
13+
func: Fluxdocs
1514
}
1615

1716
const FunctionTooltipContents: FunctionComponent<Props> = ({
18-
func: {desc, args, link, name},
17+
func: {headline, fluxParameters, name},
1918
}) => {
2019
return (
2120
<div className="flux-function-docs" data-testid={`flux-docs--${name}`}>
2221
<DapperScrollbars autoHide={false}>
2322
<div className="flux-toolbar--popover">
24-
<TooltipDescription description={desc} />
25-
<TooltipArguments argsList={args} />
26-
<TooltipLink link={link} />
23+
<TooltipDescription description={headline} />
24+
<TooltipArguments argsList={fluxParameters} />
25+
<TooltipLink />
2726
</div>
2827
</DapperScrollbars>
2928
</div>
3029
)
3130
}
32-
3331
export default FunctionTooltipContents

src/timeMachine/components/dynamicFluxFunctionsToolbar/ToolbarFunction.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ import React, {FC, createRef} from 'react'
44
// Component
55
import FunctionTooltipContents from 'src/timeMachine/components/dynamicFluxFunctionsToolbar/FunctionTooltipContents'
66
import {
7-
Popover,
8-
PopoverPosition,
9-
PopoverInteraction,
107
Appearance,
118
Button,
12-
ComponentSize,
139
ComponentColor,
10+
ComponentSize,
11+
Popover,
12+
PopoverInteraction,
13+
PopoverPosition,
1414
} from '@influxdata/clockface'
1515

1616
// Types
17-
import {FluxToolbarFunction} from 'src/types/shared'
17+
import {Fluxdocs} from 'src/client/fluxdocsdRoutes'
1818

1919
interface Props {
20-
func: FluxToolbarFunction
21-
onClickFunction: (func: FluxToolbarFunction) => void
20+
func: Fluxdocs
21+
onClickFunction: (func: Fluxdocs) => void
2222
testID: string
2323
}
2424

@@ -49,7 +49,7 @@ const ToolbarFunction: FC<Props> = ({func, onClickFunction, testID}) => {
4949
data-testid={`flux--${testID}`}
5050
className="flux-toolbar--list-item flux-toolbar--function"
5151
>
52-
<code>{func.name}</code>
52+
<code>{`${func.package}.${func.name}`}</code>
5353
<Button
5454
testID={`flux--${testID}--inject`}
5555
text="Inject"
@@ -62,7 +62,5 @@ const ToolbarFunction: FC<Props> = ({func, onClickFunction, testID}) => {
6262
</>
6363
)
6464
}
65-
6665
ToolbarFunction.defaultProps = defaultProps
67-
6866
export default ToolbarFunction
Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import React, {PureComponent} from 'react'
22

33
interface Args {
4+
headline: string
45
name: string
5-
type: string
6-
desc: string
6+
required: boolean
7+
description?: string
78
}
89

910
interface Props {
1011
argsList?: Args[]
1112
}
12-
1313
class TooltipArguments extends PureComponent<Props> {
1414
public render() {
1515
return (
@@ -19,24 +19,21 @@ class TooltipArguments extends PureComponent<Props> {
1919
</article>
2020
)
2121
}
22-
2322
private get arguments(): JSX.Element | JSX.Element[] {
2423
const {argsList} = this.props
2524

2625
if (argsList.length > 0) {
27-
return argsList.map(a => {
26+
return argsList.map(argument => {
27+
const description = argument.headline.slice(argument.name.length + 1)
2828
return (
29-
<div className="flux-function-docs--arguments" key={a.name}>
30-
<span>{a.name}:</span>
31-
<span>{a.type}</span>
32-
<div>{a.desc}</div>
29+
<div className="flux-function-docs--arguments" key={argument.name}>
30+
<span>{argument.name}:</span>
31+
<div>{description}</div>
3332
</div>
3433
)
3534
})
3635
}
37-
3836
return <div className="flux-function-docs--arguments">None</div>
3937
}
4038
}
41-
4239
export default TooltipArguments

src/timeMachine/components/dynamicFluxFunctionsToolbar/TooltipDescription.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import React, {SFC} from 'react'
1+
import React, {FC} from 'react'
22

33
interface Props {
44
description: string
55
}
66

7-
const TooltipDescription: SFC<Props> = ({description}) => (
7+
const TooltipDescription: FC<Props> = ({description}) => (
88
<article className="flux-functions-toolbar--description">
99
<div className="flux-function-docs--heading">Description</div>
1010
<span>{description}</span>
Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
import React, {SFC} from 'react'
1+
import React from 'react'
22

3-
interface Props {
4-
link: string
5-
}
6-
7-
const TooltipLink: SFC<Props> = ({link}) => (
3+
const TooltipLink = () => (
84
<p className="tooltip--link">
95
Still have questions? Check out the{' '}
10-
<a target="_blank" rel="noreferrer" href={link}>
6+
<a
7+
target="_blank"
8+
rel="noreferrer"
9+
href="https://docs.influxdata.com/flux/v0.x/stdlib/"
10+
>
1111
Flux Docs
1212
</a>
1313
.
1414
</p>
1515
)
16-
1716
export default TooltipLink

0 commit comments

Comments
 (0)