blob: bf8a24d94628cd19690772ac1fc0573570b60251 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useEffect, useState } from 'react';
import { Button, Drawer, notification, PageHeader, Select, Space } from 'antd';
import { LinkOutlined } from '@ant-design/icons';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { useIntl } from 'umi';
import { js_beautify } from 'js-beautify';
import type {Monaco} from "@monaco-editor/react";
import Editor from "@monaco-editor/react";
import type {languages} from "monaco-editor";
import { json2yaml, yaml2json } from '../../helpers';
type Props = {
visible: boolean,
readonly: boolean,
type: 'route' | 'service' | 'consumer' | 'upstream'
data: Record<string, any>,
onClose?: () => void;
onSubmit?: (data: Record<string, any>) => void;
};
enum monacoLanguageList {
JSON = 'JSON',
YAML = 'YAML',
}
const RawDataEditor: React.FC<Props> = ({ visible, readonly = true, type, data = {}, onClose = () => { }, onSubmit = () => { } }) => {
const { formatMessage } = useIntl();
const [monacoLanguage, setMonacoLanguage] = useState<PluginComponent.MonacoLanguage>(
monacoLanguageList.JSON,
);
const [content, setContent] = useState('')
useEffect(() => {
switch (monacoLanguage) {
case monacoLanguageList.JSON:
setContent(JSON.stringify(data, null, 2));
break;
case monacoLanguageList.YAML: {
const {data: yamlData} = json2yaml(JSON.stringify(data, null, 2));
setContent(yamlData)
break;
}
default:
}
}, [data])
useEffect(() => {
setMonacoLanguage(monacoLanguageList.JSON);
}, [visible])
const modeOptions = [
{ label: monacoLanguageList.JSON, value: monacoLanguageList.JSON },
{ label: monacoLanguageList.YAML, value: monacoLanguageList.YAML },
];
const handleModeChange = (value: PluginComponent.MonacoLanguage) => {
switch (value) {
case monacoLanguageList.JSON:
setContent(c => {
const {data:jsonData,error} = yaml2json(c, true);
if (error){
notification.error({message: formatMessage({ id: 'component.global.invalidYaml' })});
return c;
}
return js_beautify(jsonData, {indent_size: 2});
});
break;
case monacoLanguageList.YAML:
setContent(c => {
const {data:yamlData,error} = json2yaml(c);
if (error){
notification.error({message: formatMessage({ id: 'component.global.invalidJson' })});
return c;
}
return yamlData;
});
break;
default:
break;
}
setMonacoLanguage(value)
};
const formatYaml = (yaml: string): string=> {
const json=yaml2json(yaml,true)
if (json.error){
return yaml;
}
return json2yaml(json.data).data;
}
const editorWillMount = (monaco: Monaco) => {
const yamlFormatProvider: languages.DocumentFormattingEditProvider = {
provideDocumentFormattingEdits(model) {
return [{
text: formatYaml(model.getValue()),
range: model.getFullModelRange()
}];
}
};
monaco.languages.registerDocumentFormattingEditProvider("yaml",yamlFormatProvider);
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
trailingCommas: "error",
});
}
return (
<>
<Drawer
title={formatMessage({ id: 'component.global.data.editor' })}
placement="right"
width={700}
visible={visible}
onClose={onClose}
footer={
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Button onClick={onClose} key={1}>
{formatMessage({ id: 'component.global.cancel' })}
</Button>
<Space>
<Button
key={2}
type="primary"
onClick={() => {
try {
const editorData =
monacoLanguage === monacoLanguageList.JSON
? JSON.parse(content)
: yaml2json(content, false).data;
onSubmit(editorData);
} catch (error) {
notification.error({message: formatMessage({ id: 'component.global.invalidJson' })});
}
}}
>
{formatMessage({ id: 'component.global.submit' })}
</Button>
</Space>
</div>
}
>
<PageHeader
title=""
extra={[
<Select
defaultValue={monacoLanguageList.JSON}
value={monacoLanguage}
options={modeOptions}
onChange={(value: PluginComponent.MonacoLanguage) => {
handleModeChange(value);
}}
data-cy='monaco-language'
/>,
<CopyToClipboard text={content} onCopy={(_: string, result: boolean) => {
if (!result) {
notification.error({
message: formatMessage({ id: 'component.global.copyFail' }),
});
return;
}
notification.success({
message: formatMessage({ id: 'component.global.copySuccess' }),
});
}}>
<Button type="primary" key={2}>
{formatMessage({ id: 'component.global.copy' })}
</Button>
</CopyToClipboard>,
<Button
type="default"
icon={<LinkOutlined />}
onClick={() => {
window.open(
`https://apisix.apache.org/docs/apisix/admin-api#${type}`,
);
}}
key={1}
>
{formatMessage({ id: 'component.global.document' })}
</Button>,
]}
/>
<Editor
value={content}
onChange={text=>{
if (text){
setContent(text);
} else {
setContent('');
}
}}
onMount={(editor)=>{
// NOTE: for debug & test
// @ts-ignore
window.monacoEditor = editor;
}}
beforeMount={editorWillMount}
language={monacoLanguage.toLocaleLowerCase()}
options={{
scrollbar:{
vertical: 'hidden',
horizontal: 'hidden',
},
wordWrap: "on",
minimap: {enabled: false},
readOnly: readonly,
}}
/>
</Drawer>
</>
);
};
export default RawDataEditor;