@@ -6,46 +6,72 @@ import Stack from 'react-bootstrap/Stack';
6
6
import Form from 'react-bootstrap/Form' ;
7
7
import SyntaxHighlighter from 'react-syntax-highlighter' ;
8
8
import { atomOneLight } from 'react-syntax-highlighter/dist/esm/styles/hljs' ;
9
+ import { compareVersions } from 'compare-versions' ;
9
10
import { convertRequests , loadSchema , listFormats } from "@elastic/request-converter" ;
10
11
11
12
function App ( ) {
12
13
const formats = listFormats ( ) . sort ( ) ;
13
- const [ hasSchema , setHasSchema ] = useState ( false ) ;
14
- const [ source , setSource ] = useState ( 'GET /_search' ) ;
15
- const [ code , setCode ] = useState ( 'Loading...' ) ;
16
- const [ language , setLanguage ] = useState ( formats [ 0 ] ) ;
14
+ const [ versions , setVersions ] = useState < string [ ] > ( [ 'main' ] ) ;
15
+ const [ schemaVersion , setSchemaVersion ] = useState < string > ( 'main' ) ;
16
+ const [ hasSchema , setHasSchema ] = useState < boolean > ( false ) ;
17
+ const [ source , setSource ] = useState < string > ( 'GET /_search' ) ;
18
+ const [ error , setError ] = useState < string | null > ( null ) ;
19
+ const [ code , setCode ] = useState < string > ( 'Loading...' ) ;
20
+ const [ language , setLanguage ] = useState < string > ( formats [ 0 ] ) ;
17
21
18
22
useEffect ( ( ) => {
19
- document . getElementById ( "source" ) . focus ( ) ;
23
+ const sourceElem = document . getElementById ( "source" ) ;
24
+ if ( sourceElem ) {
25
+ sourceElem . focus ( ) ;
26
+ }
20
27
} , [ ] ) ;
21
28
22
29
useEffect ( ( ) => {
23
- ( async ( ) => {
24
- const response = await fetch ( 'https://raw.githubusercontent.com/elastic/elasticsearch-specification/refs/heads/main/output/schema/schema.json' ) ;
30
+ void ( async ( ) => {
31
+ const r = await fetch ( 'https://api.github.com/repos/elastic/elasticsearch-specification/branches' ) ;
32
+ if ( r . status === 200 ) {
33
+ const s : { name : string } [ ] = ( await r . json ( ) ) as { name : string } [ ] ;
34
+ const versions = s . map ( v => v . name ) . filter ( v => v . match ( / ^ [ 0 - 9 ] + \. [ 0 - 9 x ] + $ / ) ) ;
35
+ setVersions ( [ 'main' ] . concat ( versions . sort ( compareVersions ) . reverse ( ) ) ) ;
36
+ }
37
+ } ) ( ) ;
38
+ } , [ ] ) ;
39
+
40
+ useEffect ( ( ) => {
41
+ void ( async ( ) => {
42
+ const response = await fetch ( `https://raw.githubusercontent.com/elastic/elasticsearch-specification/refs/heads/${ schemaVersion } /output/schema/schema.json` ) ;
25
43
if ( response . ok ) {
26
- loadSchema ( await response . json ( ) ) ;
44
+ setHasSchema ( false ) ;
45
+ await loadSchema ( ( await response . json ( ) ) as object ) ;
27
46
setHasSchema ( true ) ;
28
47
}
29
48
} ) ( ) ;
30
- } , [ ] ) ;
49
+ } , [ schemaVersion ] ) ;
31
50
32
51
useEffect ( ( ) => {
33
- ( async ( ) => {
52
+ void ( async ( ) => {
34
53
if ( hasSchema ) {
35
54
let c : string = "" ;
36
55
try {
37
56
c = await convertRequests ( source , language , { complete : true , printResponse : true } ) as string ;
57
+ setError ( null ) ;
38
58
}
39
59
catch ( err ) {
60
+ if ( err instanceof Error ) {
61
+ setError ( err . toString ( ) ) ;
62
+ }
63
+ else {
64
+ setError ( 'Uknown error' ) ;
65
+ }
40
66
}
41
67
if ( c ) {
42
- setCode ( c as string ) ;
68
+ setCode ( c ) ;
43
69
}
44
70
}
45
71
} ) ( ) ;
46
72
} , [ hasSchema , source , language ] ) ;
47
73
48
- const onRequestChanged = ( ev : React . ChangeEventHandler < HTMLSelectElement > ) => {
74
+ const onRequestChanged = ( ev : React . ChangeEvent < HTMLSelectElement > ) => {
49
75
setSource ( ev . target . value ) ;
50
76
ev . target . style . height = ev . target . scrollHeight + "px" ;
51
77
} ;
@@ -55,13 +81,19 @@ function App() {
55
81
< h1 > Elasticsearch Request Converter Demo</ h1 >
56
82
< Row >
57
83
< Col className = "col-6" >
58
- Source Request
84
+ < Stack direction = "horizontal" className = "heading" >
85
+ < p > Source Request</ p >
86
+ < p className = "spacer" > </ p >
87
+ < Form . Select id = "version-choice" size = "sm" defaultValue = "main" onChange = { ev => setSchemaVersion ( ev . target . value ) } >
88
+ { versions . map ( v => < option key = { v } value = { v } > { v } </ option > ) }
89
+ </ Form . Select >
90
+ </ Stack >
59
91
</ Col >
60
92
< Col className = "col-6" >
61
93
< Stack direction = "horizontal" className = "heading" >
62
94
< p > Converted Code</ p >
63
95
< p className = "spacer" > </ p >
64
- < Form . Select id = "language-choice" size = "sm" defaultValue = { language } onChange = { ev => setLanguage ( ev . currentTarget . value ) } >
96
+ < Form . Select id = "language-choice" size = "sm" defaultValue = { language } onChange = { ev => setLanguage ( ev . target . value ) } >
65
97
{ formats . map ( fmt => < option key = { fmt } value = { fmt } > { fmt } </ option > ) }
66
98
</ Form . Select >
67
99
</ Stack >
@@ -70,7 +102,8 @@ function App() {
70
102
< Form id = "main-form" >
71
103
< Row id = "main-row" >
72
104
< Col className = "col-6" >
73
- < Form . Control as = "textarea" id = "source" value = { source } onChange = { onRequestChanged } />
105
+ < Form . Control className = { error ? "is-invalid" : "" } as = "textarea" id = "source" value = { source } onChange = { ( ev : any ) => onRequestChanged ( ev ) } />
106
+ { error && < Form . Control . Feedback type = "invalid" > { error } </ Form . Control . Feedback > }
74
107
</ Col >
75
108
< Col className = "col-6" >
76
109
< SyntaxHighlighter wrapLongLines = { true } language = { language } style = { atomOneLight } > { code } </ SyntaxHighlighter >
0 commit comments