Skip to content

Commit c64238c

Browse files
authored
feat: transform function signature to example for injection (#4269)
1 parent e93ce95 commit c64238c

File tree

6 files changed

+182
-20
lines changed

6 files changed

+182
-20
lines changed

src/flows/pipes/RawFluxEditor/FunctionsList/DynamicFunctionsList.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {getAllFluxFunctions} from 'src/shared/selectors/app'
1313

1414
// Utils
1515
import {event} from 'src/cloud/utils/reporting'
16+
import {getFluxExample} from 'src/shared/utils/fluxExample'
1617
interface Props {
1718
onSelect: (fn: FluxFunction) => void
1819
}
@@ -58,7 +59,7 @@ const DynamicFunctionsList: FC<Props> = ({onSelect}) => {
5859
}, [hoveredFunction, tooltipPopup, eventSearchTerm])
5960

6061
const handleSelectItem = useCallback((func: FluxFunction) => {
61-
onSelect(func)
62+
onSelect(getFluxExample(func))
6263
event('flux.function.injected', {name: `${func.package}.${func.name}`})
6364
}, [])
6465

src/flows/pipes/RawFluxEditor/view.tsx

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
} from '@influxdata/clockface'
1919

2020
// Types
21-
import {FluxToolbarFunction} from 'src/types/shared'
2221
import {PipeProp} from 'src/types/flows'
2322

2423
// Context
@@ -34,7 +33,6 @@ import {
3433
import SecretsList from 'src/flows/pipes/RawFluxEditor/SecretsList'
3534
import Functions from 'src/flows/pipes/RawFluxEditor/FunctionsList/GroupedFunctionsList'
3635
import DynamicFunctions from 'src/flows/pipes/RawFluxEditor/FunctionsList/DynamicFunctionsList'
37-
import {FluxFunction} from 'src/types/shared'
3836

3937
// Styles
4038
import 'src/flows/pipes/RawFluxEditor/style.scss'
@@ -75,21 +73,12 @@ const Query: FC<PipeProp> = ({Context}) => {
7573
}, [id, inject])
7674

7775
const injectIntoEditor = useCallback(
78-
(fn: FluxToolbarFunction | FluxFunction): void => {
79-
// HERE kill this
76+
(fn): void => {
8077
let text = ''
8178
if (fn.name === 'from' || fn.name === 'union') {
82-
text = `${
83-
(fn as FluxToolbarFunction).example
84-
? (fn as FluxToolbarFunction).example
85-
: fn.name + '()'
86-
}` // inject functionality for dynamic flux panels is in progress
79+
text = `${fn.example}`
8780
} else {
88-
text = ` |> ${
89-
(fn as FluxToolbarFunction).example
90-
? (fn as FluxToolbarFunction).example
91-
: fn.name + '()'
92-
}`
81+
text = ` |> ${fn.example}`
9382
}
9483

9584
const options = {
@@ -98,7 +87,6 @@ const Query: FC<PipeProp> = ({Context}) => {
9887
header: !!fn.package ? `import "${fn.package}"` : null,
9988
}
10089
inject(options)
101-
event('Inject function into Flux Script', {fn: fn.name})
10290
},
10391
[inject]
10492
)
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import {getFluxExample} from 'src/shared/utils/fluxExample'
2+
3+
describe('Flux example functions and values', () => {
4+
it('can create an example Flux value', () => {
5+
const exampleFunc = {
6+
path: 'test',
7+
package: 'test',
8+
name: 'myConstant',
9+
kind: 'Value',
10+
headline: 'A static value in a dummy test package',
11+
fluxType: 'string',
12+
description: null,
13+
fluxParameters: [],
14+
}
15+
16+
expect(getFluxExample(exampleFunc).example).toEqual(
17+
`${exampleFunc.package}.${exampleFunc.name}`
18+
)
19+
})
20+
21+
it('can create an example Flux function call with no parameters', () => {
22+
const exampleFunc = {
23+
path: 'test',
24+
package: 'test',
25+
name: 'myFunc',
26+
kind: 'Function',
27+
headline: 'A function that takes no args',
28+
fluxType: '() => string',
29+
description: null,
30+
fluxParameters: [],
31+
}
32+
33+
expect(getFluxExample(exampleFunc).example).toEqual(
34+
`${exampleFunc.package}.${exampleFunc.name}()`
35+
)
36+
})
37+
38+
it('can create an example Flux function call with only optional parameters', () => {
39+
const exampleFunc = {
40+
path: 'test',
41+
package: 'test',
42+
name: 'myFuncOptionalTwoArgs',
43+
kind: 'Function',
44+
headline: 'A function that takes two optional parameters',
45+
fluxType:
46+
'(?column:A, ?fill:B) => {reduce:(state:int, values:[E]) => int, init:(values:[D]) => int, fill:B, compute:(state:C) => C, column:A}',
47+
description: null,
48+
fluxParameters: [
49+
{
50+
name: 'column',
51+
headline: 'column: Name of the column to aggregate.',
52+
description: null,
53+
required: false,
54+
},
55+
{
56+
name: 'fill',
57+
headline: 'fill: When set, value to replace missing values.',
58+
description: null,
59+
required: false,
60+
},
61+
],
62+
}
63+
64+
expect(getFluxExample(exampleFunc).example).toEqual(
65+
`${exampleFunc.package}.${exampleFunc.name}( )`
66+
)
67+
})
68+
69+
it('can create an example Flux function call with only required parameters', () => {
70+
const exampleFunc = {
71+
path: 'test',
72+
package: 'test',
73+
name: 'myFuncRequiredThreeArgs',
74+
kind: 'Function',
75+
headline: 'A function that takes three required parameters',
76+
fluxType: '(myMeasurement:A, myField:B, myClass: string) => string',
77+
description: null,
78+
fluxParameters: [
79+
{
80+
name: 'myMeasurement',
81+
headline: 'myMeasurement: Measurement to use as training data.',
82+
description: null,
83+
required: true,
84+
},
85+
{
86+
name: 'myField',
87+
headline: 'myField: Field to use as training data.',
88+
description: null,
89+
required: true,
90+
},
91+
{
92+
name: 'myClass',
93+
headline: 'myClass: Class to classify against.',
94+
description: null,
95+
required: true,
96+
},
97+
],
98+
}
99+
100+
expect(getFluxExample(exampleFunc).example).toEqual(
101+
`${exampleFunc.package}.${exampleFunc.name}(myMeasurement: , myField: , myClass: )`
102+
)
103+
})
104+
105+
it('can create an example Flux function call with required and optional parameters', () => {
106+
const exampleFunc = {
107+
path: 'test',
108+
package: 'test',
109+
name: 'myFuncRequiredAndOptionalArgs',
110+
kind: 'Function',
111+
headline:
112+
'A function that takes two required and one optional parameters',
113+
fluxType: '(myMeasurement:A, myField:B, ?myClass: string) => string',
114+
description: null,
115+
fluxParameters: [
116+
{
117+
name: 'myMeasurement',
118+
headline: 'myMeasurement: Measurement to use as training data.',
119+
description: null,
120+
required: true,
121+
},
122+
{
123+
name: 'myField',
124+
headline: 'myField: Field to use as training data.',
125+
description: null,
126+
required: true,
127+
},
128+
{
129+
name: 'myClass',
130+
headline: 'myClass: Class to classify against.',
131+
description: null,
132+
required: false,
133+
},
134+
],
135+
}
136+
137+
expect(getFluxExample(exampleFunc).example).toEqual(
138+
`${exampleFunc.package}.${exampleFunc.name}(myMeasurement: , myField: )`
139+
)
140+
})
141+
})

src/shared/utils/fluxExample.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {FluxFunction} from 'src/types/shared'
2+
3+
export const getFluxExample = (func: FluxFunction) => {
4+
const {fluxParameters = [], kind, name, package: packageName} = func
5+
6+
let example = `${packageName}.${name}`
7+
if (kind.toLowerCase() === 'function') {
8+
let injectedParameters = ''
9+
for (const parameter of fluxParameters) {
10+
if (parameter.required) {
11+
// add a comma if the current injected list is not all spaces
12+
if (injectedParameters.trim().length !== 0) {
13+
injectedParameters = `${injectedParameters}, `
14+
}
15+
injectedParameters = `${injectedParameters}${parameter.name}: `
16+
} else {
17+
injectedParameters = `${injectedParameters} `
18+
}
19+
}
20+
example = `${packageName}.${name}(${injectedParameters})`
21+
}
22+
return {...func, example}
23+
}

src/timeMachine/components/TimeMachineFluxEditor.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@ import {
2121
generateImport,
2222
} from 'src/timeMachine/utils/insertFunction'
2323
import {event} from 'src/cloud/utils/reporting'
24+
import {isFlagEnabled} from 'src/shared/utils/featureFlag'
25+
import {getFluxExample} from 'src/shared/utils/fluxExample'
2426

2527
// Types
2628
import {FluxToolbarFunction, EditorType} from 'src/types'
2729
import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'
30+
import {FluxFunction} from 'src/types/shared'
2831

2932
const FluxEditor = lazy(() => import('src/shared/components/FluxMonacoEditor'))
3033

@@ -81,7 +84,7 @@ const TimeMachineFluxEditor: FC = () => {
8184
const defaultColumnPosition = 1 // beginning column of the row
8285

8386
const getFluxTextAndRange = (
84-
func: FluxToolbarFunction
87+
func
8588
): {text: string; range: monacoEditor.Range} => {
8689
if (!editorInstance) {
8790
return null
@@ -123,11 +126,17 @@ const TimeMachineFluxEditor: FC = () => {
123126
return {text, range}
124127
}
125128

126-
const handleInsertFluxFunction = (func: FluxToolbarFunction): void => {
129+
const handleInsertFluxFunction = (
130+
func: FluxToolbarFunction | FluxFunction
131+
): void => {
127132
if (!editorInstance) {
128133
return
129134
}
130-
const {text, range} = getFluxTextAndRange(func)
135+
const {text, range} = getFluxTextAndRange(
136+
isFlagEnabled('fluxDynamicDocs')
137+
? getFluxExample(func as FluxFunction)
138+
: func
139+
)
131140

132141
const edits = [
133142
{

src/types/shared.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ export interface Pageable {
4444
}
4545

4646
export type FluxDocs = FluxdocsRouteT
47-
export type FluxFunction = FluxDocs
47+
export type FluxFunction = FluxDocs & {example?: string}

0 commit comments

Comments
 (0)