Skip to content

Commit f5f4921

Browse files
committed
feat: Add endpoint typeahead to API Console
Closes #226
1 parent 695e4fb commit f5f4921

File tree

2 files changed

+94
-7
lines changed

2 files changed

+94
-7
lines changed

src/components/Autocomplete/Autocomplete.react.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,7 @@ export default class Autocomplete extends Component {
127127

128128
onClick(e) {
129129
const userInput = e.currentTarget.innerText;
130-
if (this.props.strict) {
131-
this.props.onChange && this.props.onChange(userInput);
132-
}
130+
this.props.onChange && this.props.onChange(userInput);
133131
const label = this.props.label || this.props.buildLabel(userInput);
134132

135133
this.inputRef.current.focus();
@@ -343,13 +341,20 @@ export default class Autocomplete extends Component {
343341

344342
let suggestionsListComponent;
345343
if (showSuggestions && !hidden && filteredSuggestions.length) {
344+
const containerWidth = this.fieldRef.current
345+
? this.fieldRef.current.offsetWidth
346+
: undefined;
347+
const mergedSuggestionsStyle = {
348+
...suggestionsStyle,
349+
...(containerWidth ? { width: containerWidth + 'px' } : {}),
350+
};
346351
suggestionsListComponent = (
347352
<SuggestionsList
348353
position={this.state.position}
349354
ref={this.dropdownRef}
350355
onExternalClick={onExternalClick}
351356
suggestions={filteredSuggestions}
352-
suggestionsStyle={suggestionsStyle}
357+
suggestionsStyle={mergedSuggestionsStyle}
353358
suggestionsItemStyle={suggestionsItemStyle}
354359
activeSuggestion={activeSuggestion}
355360
onClick={onClick}

src/dashboard/Data/ApiConsole/RestConsole.react.js

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* This source code is licensed under the license found in the LICENSE file in
66
* the root directory of this source tree.
77
*/
8+
import Autocomplete from 'components/Autocomplete/Autocomplete.react';
89
import Button from 'components/Button/Button.react';
910
import Dropdown from 'components/Dropdown/Dropdown.react';
1011
import Field from 'components/Field/Field.react';
@@ -26,6 +27,29 @@ import Toggle from 'components/Toggle/Toggle.react';
2627
import Toolbar from 'components/Toolbar/Toolbar.react';
2728
import { CurrentApp } from 'context/currentApp';
2829

30+
const PARSE_API_ENDPOINTS = [
31+
'batch',
32+
'classes/',
33+
'users',
34+
'login',
35+
'logout',
36+
'sessions',
37+
'roles',
38+
'files/',
39+
'events/',
40+
'push',
41+
'installations',
42+
'functions/',
43+
'jobs/',
44+
'schemas/',
45+
'config',
46+
'hooks/functions',
47+
'hooks/triggers',
48+
'aggregate/',
49+
'purge/',
50+
'health',
51+
];
52+
2953
export default class RestConsole extends Component {
3054
static contextType = CurrentApp;
3155
constructor() {
@@ -43,9 +67,40 @@ export default class RestConsole extends Component {
4367
inProgress: false,
4468
error: false,
4569
curlModal: false,
70+
classNames: [],
4671
};
4772
}
4873

74+
componentDidMount() {
75+
this.context
76+
.apiRequest('GET', 'schemas', {}, { useMasterKey: true })
77+
.then(({ results }) => {
78+
if (results) {
79+
this.setState({ classNames: results.map(s => s.className) });
80+
}
81+
})
82+
.catch(() => {});
83+
}
84+
85+
buildEndpointSuggestions(input) {
86+
const dynamicEndpoints = this.state.classNames.flatMap(className => [
87+
`classes/${className}`,
88+
`schemas/${className}`,
89+
`aggregate/${className}`,
90+
`purge/${className}`,
91+
]);
92+
93+
const allEndpoints = [...PARSE_API_ENDPOINTS, ...dynamicEndpoints];
94+
95+
if (!input) {
96+
return allEndpoints;
97+
}
98+
99+
return allEndpoints.filter(
100+
endpoint => endpoint.toLowerCase().indexOf(input.toLowerCase()) > -1
101+
);
102+
}
103+
49104
fetchUser() {
50105
if (this.state.runAsIdentifier.length === 0) {
51106
this.setState({ error: false, sessionToken: null });
@@ -210,11 +265,38 @@ export default class RestConsole extends Component {
210265
/>
211266
}
212267
input={
213-
<TextInput
214-
value={this.state.endpoint}
215-
monospace={true}
268+
<Autocomplete
269+
inputStyle={{
270+
width: '100%',
271+
height: '80px',
272+
textAlign: 'center',
273+
border: 'none',
274+
fontSize: '16px',
275+
fontFamily: '"Source Code Pro", "Courier New", monospace',
276+
background: 'transparent',
277+
padding: '0 6px',
278+
outline: 'none',
279+
}}
280+
containerStyle={{ width: '100%', height: 'auto' }}
281+
suggestionsStyle={{
282+
maxHeight: '200px',
283+
overflowY: 'auto',
284+
fontFamily: '"Source Code Pro", "Courier New", monospace',
285+
fontSize: '14px',
286+
borderRadius: '0 0 5px 5px',
287+
}}
288+
suggestionsItemStyle={{
289+
padding: '8px 12px',
290+
}}
216291
placeholder={'classes/_User'}
217292
onChange={endpoint => this.setState({ endpoint })}
293+
onSubmit={() => {
294+
if (!hasError) {
295+
this.makeRequest();
296+
}
297+
}}
298+
buildSuggestions={input => this.buildEndpointSuggestions(input)}
299+
buildLabel={() => ''}
218300
/>
219301
}
220302
/>

0 commit comments

Comments
 (0)