diff --git a/api/index_management/common_command.go b/api/index_management/common_command.go
new file mode 100644
index 00000000..b76bf5f9
--- /dev/null
+++ b/api/index_management/common_command.go
@@ -0,0 +1,71 @@
+package index_management
+
+import (
+ "fmt"
+ httprouter "infini.sh/framework/core/api/router"
+ "infini.sh/framework/core/elastic"
+ "infini.sh/framework/core/orm"
+ "infini.sh/framework/core/util"
+ "net/http"
+ "time"
+)
+
+func (h *APIHandler) HandleSaveCommonCommandAction(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
+ resBody := map[string]interface{}{
+ }
+
+ reqParams := elastic.CommonCommand{}
+ err := h.DecodeJSON(req, &reqParams)
+ if err != nil {
+ resBody["error"] = err
+ h.WriteJSON(w, resBody, http.StatusInternalServerError)
+ return
+ }
+
+ reqParams.Created = time.Now()
+ reqParams.ID = util.GetUUID()
+ esClient := elastic.GetClient(h.Config.Elasticsearch)
+
+ queryDSL :=[]byte(fmt.Sprintf(`{"size":1, "query":{"bool":{"must":{"match":{"title":"%s"}}}}}`, reqParams.Title))
+ var indexName = orm.GetIndexName(reqParams)
+ searchRes, err := esClient.SearchWithRawQueryDSL(indexName, queryDSL)
+ if err != nil {
+ resBody["error"] = err
+ h.WriteJSON(w, resBody, http.StatusInternalServerError)
+ return
+ }
+ if len(searchRes.Hits.Hits) > 0 {
+ resBody["error"] = "title already exists"
+ h.WriteJSON(w, resBody, http.StatusInternalServerError)
+ return
+ }
+ _, err = esClient.Index(indexName,"", reqParams.ID, reqParams)
+
+ resBody["_id"] = reqParams.ID
+ resBody["result"] = "created"
+ resBody["_source"] = reqParams
+
+ h.WriteJSON(w, resBody,http.StatusOK)
+}
+
+func (h *APIHandler) HandleQueryCommonCommandAction(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
+ resBody := map[string]interface{}{
+ }
+
+ //title := h.GetParameterOrDefault(req, "title", "")
+ //tag := h.GetParameterOrDefault(req, "search", "")
+
+ esClient := elastic.GetClient(h.Config.Elasticsearch)
+ //queryDSL :=[]byte(fmt.Sprintf(`{"query":{"bool":{"must":{"match":{"title":"%s"}}}}}`, title))
+
+ searchRes, err := esClient.SearchWithRawQueryDSL(orm.GetIndexName(elastic.CommonCommand{}),nil)
+ if err != nil {
+ resBody["error"] = err
+ h.WriteJSON(w, resBody, http.StatusInternalServerError)
+ return
+ }
+
+
+
+ h.WriteJSON(w, searchRes,http.StatusOK)
+}
diff --git a/api/init.go b/api/init.go
index 7e6ae2fa..1fb9c90e 100644
--- a/api/init.go
+++ b/api/init.go
@@ -42,6 +42,9 @@ func Init(cfg *config.AppConfig) {
ui.HandleUIMethod(api.DELETE, path.Join(esPrefix, "index/:index"), handler.HandleDeleteIndexAction)
ui.HandleUIMethod(api.POST, path.Join(esPrefix, "index/:index"), handler.HandleCreateIndexAction)
+ ui.HandleUIMethod(api.POST, path.Join(pathPrefix, "elasticsearch/command"), handler.HandleSaveCommonCommandAction)
+ ui.HandleUIMethod(api.GET, path.Join(pathPrefix, "elasticsearch/command"), handler.HandleQueryCommonCommandAction)
+
//new api
ui.HandleUIMethod(api.GET, path.Join(pathPrefix, "alerting/overview"), alerting.GetAlertOverview)
ui.HandleUIMethod(api.GET, path.Join(pathPrefix, "alerting/overview/alerts"), alerting.GetAlerts)
@@ -74,6 +77,7 @@ func Init(cfg *config.AppConfig) {
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/alerts", alerting.GetAlerts)
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_monitors/:monitorID/_acknowledge/alerts", alerting.AcknowledgeAlerts)
+
task.RegisterScheduleTask(task.ScheduleTask{
Description: "sync reindex task result",
Task: func() {
diff --git a/config/generated.go b/config/generated.go
index 65c4273d..3b59192e 100644
--- a/config/generated.go
+++ b/config/generated.go
@@ -1,8 +1,8 @@
package config
-const LastCommitLog = "N/A"
-const BuildDate = "N/A"
+const LastCommitLog = "b8fb6a3, Fri Oct 15 11:41:38 2021 +0800, liugq, console tab v0.1 "
+const BuildDate = "2021-10-21 13:55:53"
-const EOLDate = "N/A"
+const EOLDate = "2021-12-31 10:10:10"
-const Version = "0.0.1-SNAPSHOT"
+const Version = "1.0.0_SNAPSHOT"
diff --git a/web/src/assets/utility.scss b/web/src/assets/utility.scss
new file mode 100644
index 00000000..2e5a9b44
--- /dev/null
+++ b/web/src/assets/utility.scss
@@ -0,0 +1,26 @@
+.fullscreen{
+ position: fixed;
+ width: 100%;
+ left: 0;
+ top: 0;
+ height: 100vh;
+ z-index: 100;
+}
+
+@keyframes slideInUp {
+ from {
+ transform: translate3d(0, 100%, 0);
+ visibility: visible;
+ }
+
+ to {
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+.slideInUp {
+ animation-name: slideInUp;
+ animation-duration: .2s;
+ animation-fill-mode: both;
+ animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
+}
\ No newline at end of file
diff --git a/web/src/components/GlobalHeader/RightContent.js b/web/src/components/GlobalHeader/RightContent.js
index 154e0059..576d3117 100644
--- a/web/src/components/GlobalHeader/RightContent.js
+++ b/web/src/components/GlobalHeader/RightContent.js
@@ -7,8 +7,12 @@ import NoticeIcon from '../NoticeIcon';
import HeaderSearch from '../HeaderSearch';
import SelectLang from '../SelectLang';
import styles from './index.less';
+import {ConsoleUI} from '@/pages/DevTool/Console';
+import { Resizable } from "re-resizable";
+import {ResizeBar} from '@/components/infini/resize_bar';
export default class GlobalHeaderRight extends PureComponent {
+ state={consoleVisible: false}
getNoticeData() {
const { notices = [] } = this.props;
if (notices.length === 0) {
@@ -95,7 +99,10 @@ export default class GlobalHeaderRight extends PureComponent {
{
const {history, selectedCluster} = this.props;
- history.push(`/dev_tool/elasticsearch/${selectedCluster.id}/`);
+ // history.push(`/dev_tool`);
+ this.setState({
+ consoleVisible: !this.state.consoleVisible
+ })
}}>
{/*
)} */}
+
+ {/* }}
+ enable={{
+ top: true,
+ right: false,
+ bottom: false,
+ left: false,
+ topRight: false,
+ bottomRight: false,
+ bottomLeft: false,
+ topLeft: false,
+ }}> */}
+ {
+ this.setState({
+ consoleVisible: false,
+ })
+ }}
+ clusterStatus={this.props.clusterStatus}
+ resizeable={true}
+ />
+ {/* */}
+
);
}
}
+
+const TopHandle = () => {
+ return hello world
;
+};
\ No newline at end of file
diff --git a/web/src/components/infini/health_status_circle.tsx b/web/src/components/infini/health_status_circle.tsx
index a8dc4bf1..497a15ce 100644
--- a/web/src/components/infini/health_status_circle.tsx
+++ b/web/src/components/infini/health_status_circle.tsx
@@ -1,4 +1,5 @@
-export type ClusterHealthStatus = 'green' | 'yellow' | 'red';
+import {Icon} from 'antd';
+export type ClusterHealthStatus = 'green' | 'yellow' | 'red' | 'unavailable';
const statusColorMap: Record = {
'green': '#39b362',
@@ -15,6 +16,9 @@ interface props {
}
export const HealthStatusCircle = ({status}: props)=>{
+ if(status == 'unavailable'){
+ return
+ }
const color = convertStatusToColor(status);
return
}
\ No newline at end of file
diff --git a/web/src/components/infini/resize_bar.js b/web/src/components/infini/resize_bar.js
new file mode 100644
index 00000000..d3bbde52
--- /dev/null
+++ b/web/src/components/infini/resize_bar.js
@@ -0,0 +1,9 @@
+import './resize_bar.scss';
+
+export const ResizeBar = () => {
+ return ;
+};
\ No newline at end of file
diff --git a/web/src/components/infini/resize_bar.scss b/web/src/components/infini/resize_bar.scss
new file mode 100644
index 00000000..ffa4abd4
--- /dev/null
+++ b/web/src/components/infini/resize_bar.scss
@@ -0,0 +1,13 @@
+.resize-bar{
+ display: flex;
+ height: 10px;
+ background: #eee;
+ border-top: 1px solid #bbb;
+ align-items: center;
+ justify-content: center;
+ .dash{
+ height: 2px;
+ width: 24px;
+ background: #999;
+ }
+}
\ No newline at end of file
diff --git a/web/src/components/kibana/console/components/CommonCommandModal.tsx b/web/src/components/kibana/console/components/CommonCommandModal.tsx
index a44b5e75..0b3b54fe 100644
--- a/web/src/components/kibana/console/components/CommonCommandModal.tsx
+++ b/web/src/components/kibana/console/components/CommonCommandModal.tsx
@@ -66,7 +66,7 @@ const CommonCommandModal = Form.create()((props: ICommonCommandModalProps) => {
};
return (
-
+
{form.getFieldDecorator('title', {
diff --git a/web/src/components/kibana/console/components/Console.tsx b/web/src/components/kibana/console/components/Console.tsx
index 46c24bee..ef08fb6e 100644
--- a/web/src/components/kibana/console/components/Console.tsx
+++ b/web/src/components/kibana/console/components/Console.tsx
@@ -1,5 +1,5 @@
// @ts-ignore
-import React, { useRef, useMemo,useEffect } from 'react';
+import React, { useRef, useMemo,useEffect, useLayoutEffect } from 'react';
import ConsoleInput from './ConsoleInput';
import ConsoleOutput from './ConsoleOutput';
import { Panel } from './Panel';
@@ -14,12 +14,14 @@ import { createHistory, History, createStorage, createSettings } from '../servic
import { create } from '../storage/local_storage_object_client';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import {RequestStatusBar} from './request_status_bar';
+import useEventListener from '@/lib/hooks/use_event_listener';
interface props {
selectedCluster: any;
saveEditorContent: (content: string)=>void;
initialText: string;
paneKey: string;
+ height: number;
}
const INITIAL_PANEL_WIDTH = 50;
@@ -30,8 +32,8 @@ const ConsoleWrapper = ({
saveEditorContent,
initialText,
paneKey,
+ height,
}:props) => {
-
const {
requestInFlight: requestInProgress,
lastResult: { data: requestData, error: requestError },
@@ -53,26 +55,36 @@ const ConsoleWrapper = ({
const statusBarRef = useRef(null);
const consoleRef = useRef(null);
- useEffect(()=>{
- statusBarRef.current && consoleRef.current && (statusBarRef.current.style.width=consoleRef.current.offsetWidth+'px');
- const winScroll = ()=>{
- const wsTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
- if(wsTop>getElementTop(consoleRef.current)) {
- statusBarRef.current && (statusBarRef.current.style.position='relative');
- }else{
- statusBarRef.current && (statusBarRef.current.style.position='fixed');
- }
- }
- window.addEventListener('scroll', winScroll)
- return ()=>{
- window.removeEventListener('scroll', winScroll)
- }
- },[])
+ // useEffect(()=>{
+ // const winScroll = ()=>{
+ // const wsTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
+ // if(wsTop>getElementTop(consoleRef.current)) {
+ // statusBarRef.current && (statusBarRef.current.style.position='relative');
+ // }else{
+ // statusBarRef.current && (statusBarRef.current.style.position='fixed');
+ // }
+ // }
+ // window.addEventListener('scroll', winScroll, {passive:true})
+ // return ()=>{
+ // window.removeEventListener('scroll', winScroll)
+ // }
+ // },[])
+ useEventListener('resize', ()=>{
+ statusBarRef.current && consoleRef.current && (statusBarRef.current.style.width=consoleRef.current.offsetWidth+'px');
+ })
+
+ useLayoutEffect(()=>{
+ // console.log(consoleRef.current?.offsetWidth)
+ if(consoleRef.current.offsetWidth>0)
+ statusBarRef.current && consoleRef.current && (statusBarRef.current.style.width=consoleRef.current.offsetWidth+'px');
+ }, [consoleRef.current?.offsetWidth])
+
+ const calcHeight = height > 0 ? (height-35)+'px' : '100%';
return (
-
-
+
+
@@ -82,12 +94,8 @@ const ConsoleWrapper = ({
-
);
};
const Console = (params:props) => {
+
const registryRef = useRef(new PanelRegistry());
// const [consoleInputKey] = useMemo(()=>{
// return [selectedCluster.id + '-console-input'];
diff --git a/web/src/components/kibana/console/components/ConsoleInput.tsx b/web/src/components/kibana/console/components/ConsoleInput.tsx
index d5f6f11b..d0ea57c1 100644
--- a/web/src/components/kibana/console/components/ConsoleInput.tsx
+++ b/web/src/components/kibana/console/components/ConsoleInput.tsx
@@ -11,7 +11,7 @@ import './ConsoleInput.scss';
import { useSendCurrentRequestToES } from '../hooks/use_send_current_request_to_es';
import { useSetInputEditor } from '../hooks/use_set_input_editor';
import '@elastic/eui/dist/eui_theme_light.css';
-import { instance as registry, editorList } from '../contexts/editor_context/editor_registry';
+import { instance as registry } from '../contexts/editor_context/editor_registry';
import 'antd/dist/antd.css';
import {retrieveAutoCompleteInfo} from '../modules/mappings/mappings';
import {useSaveCurrentTextObject} from '../hooks/use_save_current_text_object';
@@ -109,7 +109,6 @@ const ConsoleInputUI = ({clusterID, initialText, saveEditorContent, paneKey}:Con
editorInstanceRef.current = senseEditor;
setInputEditor(senseEditor);
senseEditor.paneKey = paneKey;
- editorList.addInputEditor(senseEditor);
senseEditor.update(initialText || DEFAULT_INPUT_VALUE);
applyCurrentSettings(senseEditor!.getCoreEditor(), {fontSize:12, wrapMode: true,});
@@ -151,8 +150,12 @@ const ConsoleInputUI = ({clusterID, initialText, saveEditorContent, paneKey}:Con
aceEditorRef.current && (aceEditorRef.current['clusterID'] = clusterID);
},[clusterID])
- const handleSaveAsCommonCommand = async () => {
- const editor = registry.getInputEditor();
+ const handleSaveAsCommonCommand = async () => {
+ const editor = editorInstanceRef.current;
+ if(editor == null){
+ console.warn('editor is null')
+ return
+ }
const requests = await editor.getRequestsInRange();
const formattedRequest = requests.map(request => ({
method: request.method,
diff --git a/web/src/components/kibana/console/components/request_status_bar/request_status_bar.scss b/web/src/components/kibana/console/components/request_status_bar/request_status_bar.scss
new file mode 100644
index 00000000..28841945
--- /dev/null
+++ b/web/src/components/kibana/console/components/request_status_bar/request_status_bar.scss
@@ -0,0 +1,28 @@
+.request-status-bar{
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+ .bar-item{
+ flex: 0 0 50%;
+ .base-info{
+ display: flex;
+ font-size: 12px;
+ .info-item{
+ margin: 12px;
+ position: relative;
+ &.health{
+ padding-right: 14px;
+ }
+ }
+ }
+ .status_info{
+ display: flex;
+ font-size: 12px;
+ align-items: center;
+ .info-item{
+ margin: 12px;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/src/components/kibana/console/components/request_status_bar/request_status_bar.tsx b/web/src/components/kibana/console/components/request_status_bar/request_status_bar.tsx
index 0d98f85d..00a0baa5 100644
--- a/web/src/components/kibana/console/components/request_status_bar/request_status_bar.tsx
+++ b/web/src/components/kibana/console/components/request_status_bar/request_status_bar.tsx
@@ -18,7 +18,10 @@
*/
import React, { FunctionComponent } from 'react';
-import { EuiFlexGroup, EuiFlexItem, EuiBadge, EuiText, EuiToolTip } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiBadge, EuiText, EuiToolTip,EuiCodeBlock } from '@elastic/eui';
+import {HealthStatusCircle} from '@/components/infini/health_status_circle';
+import './request_status_bar.scss';
+import {Drawer, Tabs, Button} from 'antd';
export interface Props {
requestInProgress: boolean;
@@ -37,6 +40,8 @@ export interface Props {
// The time, in milliseconds, that the last request took
timeElapsedMs: number;
+ responseHeader: string;
+ requestHeader: string;
};
}
@@ -60,17 +65,103 @@ const mapStatusCodeToBadgeColor = (statusCode: number) => {
return 'danger';
};
-export const RequestStatusBar: FunctionComponent
= ({
+// export const RequestStatusBar: FunctionComponent = ({
+// requestInProgress,
+// requestResult,
+// selectedCluster,
+// }) => {
+// let content: React.ReactNode = null;
+// const clusterContent = (
+//
+// {selectedCluster.host} - {selectedCluster.version}
+//
+// );
+
+// if (requestInProgress) {
+// content = (
+//
+//
+// Request in progress
+//
+//
+// );
+// } else if (requestResult) {
+// const { endpoint, method, statusCode, statusText, timeElapsedMs } = requestResult;
+
+// content = (
+// <>
+//
+// {`${method} ${
+// endpoint.startsWith('/') ? endpoint : '/' + endpoint
+// }`}
+// }
+// >
+//
+// {/* Use to ensure that no matter the width we don't allow line breaks */}
+// {statusCode} - {statusText}
+//
+//
+//
+//
+//
+// Time Elapsed
+//
+// }
+// >
+//
+//
+// {timeElapsedMs} {'ms'}
+//
+//
+//
+//
+// >
+// );
+// }
+
+// return (
+//
+// {clusterContent}
+// {content}
+//
+// );
+// };
+
+export const RequestStatusBar = ({
requestInProgress,
requestResult,
selectedCluster,
-}) => {
+}:Props) => {
let content: React.ReactNode = null;
- const clusterContent = (
-
- {selectedCluster.host} - {selectedCluster.version}
-
-);
+ const clusterContent = (
+
+ 健康状态:
+
+
+
+
+
+ 集群地址:
+ {selectedCluster.host}
+
+
+ 版本:
+ {selectedCluster.version}
+
+
);
+const [headerInfoVisible, setHeaderInfoVisible] = React.useState(false)
if (requestInProgress) {
content = (
@@ -85,7 +176,8 @@ export const RequestStatusBar: FunctionComponent = ({
content = (
<>
-
+
+
= ({
{statusCode} - {statusText}
-
-
+
+
= ({
-
+
+
+
+
+
+
+
>
);
}
return (
-
- {clusterContent}
- {content}
-
+
+
{clusterContent}
+
{content}
+
{setHeaderInfoVisible(false)}}
+ >
+
+
+
+
+ {requestResult?.requestHeader}
+
+
+
+
+
+
+ {requestResult?.responseHeader}
+
+
+
+
+
+
);
};
diff --git a/web/src/components/kibana/console/contexts/editor_context/editor_registry.ts b/web/src/components/kibana/console/contexts/editor_context/editor_registry.ts
index b063cdeb..65eb4b8c 100644
--- a/web/src/components/kibana/console/contexts/editor_context/editor_registry.ts
+++ b/web/src/components/kibana/console/contexts/editor_context/editor_registry.ts
@@ -37,7 +37,6 @@ export class EditorRegistry {
setInputEditor(inputEditor: SenseEditor) {
this.inputEditor = inputEditor;
- inputEditor.setAutocompleter();
}
getInputEditor() {
diff --git a/web/src/components/kibana/console/entities/core_editor.ts b/web/src/components/kibana/console/entities/core_editor.ts
index 09c6cacb..b1e092ff 100644
--- a/web/src/components/kibana/console/entities/core_editor.ts
+++ b/web/src/components/kibana/console/entities/core_editor.ts
@@ -1,5 +1,6 @@
import { TokensProvider } from './tokens_provider';
import { Token } from './token';
+import RowParser from './row_parser';
type MarkerRef = any;
@@ -81,6 +82,9 @@ export enum LINE_MODE {
* being used which is usually vendor code such as Ace or Monaco.
*/
export interface CoreEditor {
+ getParser(): RowParser;
+ getAutocompleter(): AutoCompleterFunction;
+
/**
* Get the current position of the cursor.
*/
diff --git a/web/src/components/kibana/console/entities/sense_editor.ts b/web/src/components/kibana/console/entities/sense_editor.ts
index 8a01e0ce..a734920c 100644
--- a/web/src/components/kibana/console/entities/sense_editor.ts
+++ b/web/src/components/kibana/console/entities/sense_editor.ts
@@ -12,16 +12,16 @@ export class SenseEditor {
currentReqRange: (Range & { markerRef: unknown }) | null;
parser: RowParser;
- private readonly autocomplete: ReturnType;
+ // private readonly autocomplete: ReturnType;
constructor(private readonly coreEditor: CoreEditor) {
this.currentReqRange = null;
- this.parser = new RowParser(this.coreEditor);
- this.autocomplete = createAutocompleter({
- coreEditor,
- parser: this.parser,
- });
- this.coreEditor.registerAutocompleter(this.autocomplete.getCompletions);
+ // this.parser = new RowParser(this.coreEditor);
+ // this.autocomplete = createAutocompleter({
+ // coreEditor,
+ // });
+ // this.coreEditor.registerAutocompleter(this.autocomplete.getCompletions);
+ this.parser = coreEditor.getParser();
this.coreEditor.on(
'tokenizerUpdate',
this.highlightCurrentRequestsAndUpdateActionBar.bind(this)
@@ -30,10 +30,6 @@ export class SenseEditor {
this.coreEditor.on('changeScrollTop', this.updateActionsBar.bind(this));
}
- setAutocompleter = ()=>{
- this.coreEditor.registerAutocompleter(this.autocomplete.getCompletions);
- }
-
prevRequestStart = (rowOrPos?: number | Position): Position => {
let curRow: number;
diff --git a/web/src/components/kibana/console/hooks/use_send_current_request_to_es/index.ts b/web/src/components/kibana/console/hooks/use_send_current_request_to_es/index.ts
index 0f3b04de..e160894d 100644
--- a/web/src/components/kibana/console/hooks/use_send_current_request_to_es/index.ts
+++ b/web/src/components/kibana/console/hooks/use_send_current_request_to_es/index.ts
@@ -36,6 +36,7 @@ import { instance as registry } from '../../contexts/editor_context/editor_regis
import { useRequestActionContext } from '../../contexts/request_context';
import { useServicesContext } from '../../contexts/services_context';
import {getCommand} from '../../modules/mappings/mappings';
+import {useEditorReadContext} from '../../contexts/editor_context';
function buildRawCommonCommandRequest(cmd:any){
const {requests} = cmd._source;
@@ -48,10 +49,12 @@ function buildRawCommonCommandRequest(cmd:any){
export const useSendCurrentRequestToES = () => {
const dispatch = useRequestActionContext();
const { services: { history }, clusterID } = useServicesContext();
+ const {sensorEditor:editor} = useEditorReadContext();
return useCallback(async () => {
try {
- const editor = registry.getInputEditor();
+ // const editor = registry.getInputEditor();
+ if(!editor) return
const requests = await editor.getRequestsInRange();
if (!requests.length) {
console.log('No request selected. Select a request by placing the cursor inside it.');
@@ -118,5 +121,5 @@ export const useSendCurrentRequestToES = () => {
});
}
}
- }, [dispatch, history, clusterID]);
+ }, [dispatch, history, clusterID, editor]);
};
diff --git a/web/src/components/kibana/console/hooks/use_send_current_request_to_es/send_request_to_es.ts b/web/src/components/kibana/console/hooks/use_send_current_request_to_es/send_request_to_es.ts
index 9fa2cf30..fb20e22c 100644
--- a/web/src/components/kibana/console/hooks/use_send_current_request_to_es/send_request_to_es.ts
+++ b/web/src/components/kibana/console/hooks/use_send_current_request_to_es/send_request_to_es.ts
@@ -74,7 +74,6 @@ export function sendRequestToES(args: EsRequestArgs): Promise
if (reqId !== CURRENT_REQ_ID) {
return;
}
-
const xhr = dataOrjqXHR.promise ? dataOrjqXHR : jqXhrORerrorThrown;
const isSuccess =
@@ -83,7 +82,10 @@ export function sendRequestToES(args: EsRequestArgs): Promise
((xhr.status >= 200 && xhr.status < 300) || xhr.status === 404);
if (isSuccess) {
- let value = xhr.responseText;
+ // let value = xhr.responseText;
+ let resObj = JSON.parse(xhr.responseText)
+
+ let value = resObj.response_body;
const warnings = xhr.getResponseHeader('warning');
if (warnings) {
@@ -102,11 +104,13 @@ export function sendRequestToES(args: EsRequestArgs): Promise
statusText: xhr.statusText,
contentType: xhr.getResponseHeader('Content-Type'),
value,
+ header: resObj.response_header,
},
request: {
data: esData,
method: esMethod,
path: esPath,
+ header: resObj.request_header,
},
});
@@ -116,8 +120,15 @@ export function sendRequestToES(args: EsRequestArgs): Promise
let value;
let contentType: string;
if (xhr.responseText) {
- value = xhr.responseText; // ES error should be shown
- contentType = xhr.getResponseHeader('Content-Type');
+ const resObj = JSON.parse(xhr.responseText)
+ if(resObj.error){
+ value = resObj.error;
+ contentType = 'text/plain';
+ }else{
+ value = resObj.response_body; // ES error should be shown
+ contentType = xhr.getResponseHeader('Content-Type');
+ }
+
} else {
value = 'Request failed to get to the server (status code: ' + xhr.status + ')';
contentType = 'text/plain';
diff --git a/web/src/components/kibana/console/hooks/use_set_input_editor.ts b/web/src/components/kibana/console/hooks/use_set_input_editor.ts
index d1c6d150..651358df 100644
--- a/web/src/components/kibana/console/hooks/use_set_input_editor.ts
+++ b/web/src/components/kibana/console/hooks/use_set_input_editor.ts
@@ -41,7 +41,7 @@ export const useSetInputEditor = () => {
return useCallback(
(editor: SenseEditor) => {
dispatch({ type: 'setInputEditor', payload: editor });
- registry.setInputEditor(editor);
+ // registry.setInputEditor(editor);
},
[dispatch]
);
diff --git a/web/src/components/kibana/console/modules/autocomplete/autocomplete.ts b/web/src/components/kibana/console/modules/autocomplete/autocomplete.ts
index 6d7d4595..679010b3 100644
--- a/web/src/components/kibana/console/modules/autocomplete/autocomplete.ts
+++ b/web/src/components/kibana/console/modules/autocomplete/autocomplete.ts
@@ -360,13 +360,10 @@ function addMetaToTermsList(
// eslint-disable-next-line
export default function ({
coreEditor: editor,
- parser,
}: {
coreEditor: CoreEditor;
- parser: RowParser;
}) {
-
-
+ const parser = new RowParser(editor)
function applyTerm(term: {
value?: string;
context?: AutoCompleteContext;
diff --git a/web/src/components/kibana/console/modules/es/index.ts b/web/src/components/kibana/console/modules/es/index.ts
index 43d00643..8bbec3fc 100644
--- a/web/src/components/kibana/console/modules/es/index.ts
+++ b/web/src/components/kibana/console/modules/es/index.ts
@@ -34,6 +34,7 @@
import $ from 'jquery';
// @ts-ignore
import { stringify } from 'query-string';
+import {pathPrefix} from '@/services/common';
interface SendOptions {
asSystemRequest?: boolean;
@@ -103,12 +104,7 @@ export function send(
}
export function queryCommonCommands(title?: string) {
- const clusterID = extractClusterIDFromURL();
- if(!clusterID){
- console.log('can not get clusterid from url');
- return;
- }
- let url = `/elasticsearch/${clusterID}/command/_search`;
+ let url = `${pathPrefix}/elasticsearch/command`;
if(title){
url +=`?title=${title}`
}
@@ -124,12 +120,7 @@ export function constructESUrl(baseUri: string, path: string) {
}
export function saveCommonCommand(params: any) {
- const clusterID = extractClusterIDFromURL();
- if(!clusterID){
- console.log('can not get clusterid from url');
- return;
- }
- return fetch(`/elasticsearch/${clusterID}/command`, {
+ return fetch(`${pathPrefix}/elasticsearch/command`, {
method: 'POST',
body: JSON.stringify(params),
headers:{
diff --git a/web/src/components/kibana/console/modules/legacy_core_editor/legacy_core_editor.ts b/web/src/components/kibana/console/modules/legacy_core_editor/legacy_core_editor.ts
index 16a7967f..148b8f8c 100644
--- a/web/src/components/kibana/console/modules/legacy_core_editor/legacy_core_editor.ts
+++ b/web/src/components/kibana/console/modules/legacy_core_editor/legacy_core_editor.ts
@@ -10,28 +10,58 @@ import { EditorEvent, AutoCompleterFunction } from '../../entities/core_editor';
import { AceTokensProvider } from '../../entities/ace_tokens_providers';
import * as curl from './curl';
import smartResize from './smart_resize';
-ace.define(
- 'ace/autocomplete/text_completer',
- ['require', 'exports', 'module'],
- function (
- require: unknown,
- exports: {
- getCompletions: (
- innerEditor: unknown,
- session: unknown,
- pos: unknown,
- prefix: unknown,
- callback: (e: null | Error, values: string[]) => void
- ) => void;
- }
- ) {
- exports.getCompletions = function (innerEditor, session, pos, prefix, callback) {
- callback(null, []);
- };
- }
-);
+import createAutocompleter from '../../modules/autocomplete/autocomplete';
+import RowParser from '../../entities/row_parser';
-const langTools = ace.acequire('ace/ext/language_tools');
+(function initAceEditor() {
+ ace.define(
+ 'ace/autocomplete/text_completer',
+ ['require', 'exports', 'module'],
+ function (
+ require: unknown,
+ exports: {
+ getCompletions: (
+ innerEditor: unknown,
+ session: unknown,
+ pos: unknown,
+ prefix: unknown,
+ callback: (e: null | Error, values: string[]) => void
+ ) => void;
+ }
+ ) {
+ exports.getCompletions = function (innerEditor, session, pos, prefix, callback) {
+ callback(null, []);
+ };
+ }
+ );
+
+ const langTools = ace.acequire('ace/ext/language_tools');
+
+ langTools.setCompleters( //addCompleters
+ [{
+ identifierRegexps: [
+ /[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character
+ ],
+ getCompletions: (
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ DO_NOT_USE_1: IAceEditor,
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ DO_NOT_USE_2: IAceEditSession,
+ pos: { row: number; column: number },
+ prefix: string,
+ callback: (...args: unknown[]) => void
+ ) => {
+ const {coreEditor} = DO_NOT_USE_1;
+ const position: Position = {
+ lineNumber: pos.row + 1,
+ column: pos.column + 1,
+ };
+ coreEditor.autocompleter(position, prefix, callback);
+ // autocompleter(position, prefix, callback);
+ },
+ }],
+ );
+})();
// @ts-ignore
import * as InputMode from './mode/input';
@@ -45,10 +75,16 @@ export class LegacyCoreEditor implements CoreEditor {
// @ts-ignore
$actions: JQuery;
resize: () => void;
+ private autocompleter: AutoCompleterFunction;
+ private parser: RowParser;
- constructor(private readonly editor: IAceEditor, actions: HTMLElement) {
+ constructor(private editor: IAceEditor, actions: HTMLElement) {
this.$actions = $(actions);
this.editor.setShowPrintMargin(false);
+ this.parser = new RowParser(this);
+ this.autocompleter = createAutocompleter({
+ coreEditor: this,
+ }).getCompletions
const session = this.editor.getSession();
// @ts-ignore
@@ -72,6 +108,14 @@ export class LegacyCoreEditor implements CoreEditor {
this.editor.$blockScrolling = Infinity;
this.hideActionsBar();
this.editor.focus();
+ editor.coreEditor = this;
+ }
+
+ getParser(): RowParser {
+ return this.parser;
+ }
+ getAutocompleter(): AutoCompleterFunction {
+ return this.autocompleter;
}
// dirty check for tokenizer state, uses a lot less cycles
@@ -388,6 +432,7 @@ export class LegacyCoreEditor implements CoreEditor {
prefix: string,
callback: (...args: unknown[]) => void
) => {
+ debugger
const position: Position = {
lineNumber: pos.row + 1,
column: pos.column + 1,
diff --git a/web/src/components/kibana/console/modules/mappings/mappings.js b/web/src/components/kibana/console/modules/mappings/mappings.js
index c4675e3a..f446b925 100644
--- a/web/src/components/kibana/console/modules/mappings/mappings.js
+++ b/web/src/components/kibana/console/modules/mappings/mappings.js
@@ -223,15 +223,23 @@ function getFieldNamesFromProperties(properties = {}) {
}
function loadTemplates(templatesObject = {}, clusterID) {
+ templatesObject = getRawBody(templatesObject);
templates[clusterID] = Object.keys(templatesObject);
}
+function getRawBody(body) {
+ if(body.response_body){
+ return JSON.parse(body.response_body);
+ }
+ return body;
+}
+
export function loadMappings(mappings, clusterID) {
+ mappings = getRawBody(mappings)
let clusterPerIndexTypes = {};
$.each(mappings, function (index, indexMapping) {
const normalizedIndexMappings = {};
-
// Migrate 1.0.0 mappings. This format has changed, so we need to extract the underlying mapping.
if (indexMapping.mappings && _.keys(indexMapping).length === 1) {
indexMapping = indexMapping.mappings;
@@ -242,7 +250,7 @@ export function loadMappings(mappings, clusterID) {
const fieldList = getFieldNamesFromProperties(typeMapping);
normalizedIndexMappings[typeName] = fieldList;
} else {
- normalizedIndexMappings[typeName] = [];
+ normalizedIndexMappings[typeName] = getFieldNamesFromProperties(typeMapping.properties); // for es 2.x, 5.x, 6.x
}
});
clusterPerIndexTypes[index] = normalizedIndexMappings;
@@ -251,6 +259,7 @@ export function loadMappings(mappings, clusterID) {
}
export function loadAliases(aliases, clusterID) {
+ aliases = getRawBody(aliases)
let clusterPerAliasIndexes = {};
$.each(aliases || {}, function (index, omdexAliases) {
// verify we have an index defined. useful when mapping loading is disabled
diff --git a/web/src/components/kibana/console/stores/editor.ts b/web/src/components/kibana/console/stores/editor.ts
index e73ad115..06553b69 100644
--- a/web/src/components/kibana/console/stores/editor.ts
+++ b/web/src/components/kibana/console/stores/editor.ts
@@ -39,12 +39,14 @@ import { SenseEditor } from '../entities/sense_editor';
export interface Store {
ready: boolean;
currentTextObject: TextObject | null;
+ sensorEditor: SenseEditor | null;
}
export const initialValue: Store = produce(
{
ready: false,
currentTextObject: null,
+ sensorEditor: null,
},
identity
);
@@ -58,7 +60,8 @@ export const reducer: Reducer = (state, action) =>
if (action.type === 'setInputEditor') {
if (action.payload) {
draft.ready = true;
- }
+ draft.sensorEditor = action.payload;
+ }
return;
}
diff --git a/web/src/lib/hooks/storage.js b/web/src/lib/hooks/storage.js
index ac9805dd..2eb60de5 100644
--- a/web/src/lib/hooks/storage.js
+++ b/web/src/lib/hooks/storage.js
@@ -16,7 +16,7 @@ function useStorage(key, defaultValue, storageObject, encryptor) {
return storeValue
}
- if (typeof initialValue === "function") {
+ if (typeof defaultValue === "function") {
return defaultValue()
} else {
return defaultValue
diff --git a/web/src/lib/hooks/use_event_listener.js b/web/src/lib/hooks/use_event_listener.js
new file mode 100644
index 00000000..772ea587
--- /dev/null
+++ b/web/src/lib/hooks/use_event_listener.js
@@ -0,0 +1,21 @@
+import { useEffect, useRef } from "react"
+
+export default function useEventListener(
+ eventType,
+ callback,
+ element = window
+) {
+ const callbackRef = useRef(callback)
+
+ useEffect(() => {
+ callbackRef.current = callback
+ }, [callback])
+
+ useEffect(() => {
+ if (element == null) return
+ const handler = e => callbackRef.current(e)
+ element.addEventListener(eventType, handler)
+
+ return () => element.removeEventListener(eventType, handler)
+ }, [eventType, element])
+}
\ No newline at end of file
diff --git a/web/src/lib/hooks/use_timeout.js b/web/src/lib/hooks/use_timeout.js
new file mode 100644
index 00000000..3b55c9e4
--- /dev/null
+++ b/web/src/lib/hooks/use_timeout.js
@@ -0,0 +1,30 @@
+import { useCallback, useEffect, useRef } from "react"
+
+export default function useTimeout(callback, delay) {
+ const callbackRef = useRef(callback)
+ const timeoutRef = useRef()
+
+ useEffect(() => {
+ callbackRef.current = callback
+ }, [callback])
+
+ const set = useCallback(() => {
+ timeoutRef.current = setTimeout(() => callbackRef.current(), delay)
+ }, [delay])
+
+ const clear = useCallback(() => {
+ timeoutRef.current && clearTimeout(timeoutRef.current)
+ }, [])
+
+ useEffect(() => {
+ set()
+ return clear
+ }, [delay, set, clear])
+
+ const reset = useCallback(() => {
+ clear()
+ set()
+ }, [clear, set])
+
+ return { reset, clear }
+}
\ No newline at end of file
diff --git a/web/src/models/global.js b/web/src/models/global.js
index bf19b45d..3f869dd1 100644
--- a/web/src/models/global.js
+++ b/web/src/models/global.js
@@ -4,6 +4,7 @@ import {searchClusterConfig, getClusterStatus} from "@/services/cluster";
import {formatESSearchResult, extractClusterIDFromURL} from '@/lib/elasticsearch/util';
import {Modal} from 'antd';
import router from "umi/router";
+import _ from 'lodash';
const MENU_COLLAPSED_KEY = "search-center:menu:collapsed";
@@ -153,21 +154,24 @@ export default {
}
}
},
- *fetchClusterStatus({payload}, {call, put}){
+ *fetchClusterStatus({payload}, {call, put, select}){
let res = yield call(getClusterStatus, payload);
if(!res){
return false
}
+ const {clusterStatus} = yield select(state=>state.global);
if(res.error){
console.log(res.error)
return false;
}
- yield put({
- type: 'saveData',
- payload: {
- clusterStatus: res
- }
- });
+ if(!_.isEqual(res, clusterStatus)){
+ yield put({
+ type: 'saveData',
+ payload: {
+ clusterStatus: res
+ }
+ });
+ }
return res;
},
},
@@ -251,7 +255,7 @@ export default {
// Subscribe history(url) change, trigger `load` action if pathname is `/`
return history.listen(({ pathname, search }) => {
let clusterVisible = true;
- const clusterHiddenPath = ["/system", "/cluster/overview", "/alerting/overview", "/alerting/monitor/monitors/", "/alerting/destination"];
+ const clusterHiddenPath = ["/system", "/cluster/overview", "/alerting/overview", "/alerting/monitor/monitors/", "/alerting/destination", '/dev_tool'];
if(clusterHiddenPath.some(p=>pathname.startsWith(p))){
clusterVisible = false;
if(pathname.includes("elasticsearch")){
diff --git a/web/src/pages/DevTool/Console.tsx b/web/src/pages/DevTool/Console.tsx
index f687f58a..be144617 100644
--- a/web/src/pages/DevTool/Console.tsx
+++ b/web/src/pages/DevTool/Console.tsx
@@ -1,10 +1,13 @@
import Console from '../../components/kibana/console/components/Console';
import {connect} from 'dva';
import {Tabs, Button, Icon, Menu, Dropdown} from 'antd';
-import {useState, useReducer, useCallback, useEffect, useMemo} from 'react';
+import {useState, useReducer, useCallback, useEffect, useMemo, useRef, useLayoutEffect} from 'react';
import {useLocalStorage} from '@/lib/hooks/storage';
import {setClusterID} from '../../components/kibana/console/modules/mappings/mappings';
-import {editorList} from '@/components/kibana/console/contexts/editor_context/editor_registry';
+import {TabTitle} from './console_tab_title';
+import '@/assets/utility.scss';
+import { Resizable } from "re-resizable";
+import {ResizeBar} from '@/components/infini/resize_bar';
const { TabPane } = Tabs;
@@ -25,7 +28,7 @@ const addTab = (state: any, action: any) => {
const { panes } = state;
const {cluster} = action.payload;
const activeKey = `${cluster.id}:${new Date().valueOf()}`;
- panes.push({ key: activeKey, cluster_id: cluster.id});
+ panes.push({ key: activeKey, cluster_id: cluster.id, title: cluster.name});
return {
...state,
panes,
@@ -58,7 +61,22 @@ const consoleTabReducer = (state: any, action: any) => {
...state,
activeKey: payload.activeKey,
}
- editorList.setActiveEditor(payload.activeKey);
+ break;
+ case 'saveTitle':
+ const {key, title} = action.payload;
+ const newPanes = state.panes.map((pane: any)=>{
+ if(pane.key == key){
+ return {
+ ...pane,
+ title,
+ }
+ }
+ return pane;
+ });
+ newState = {
+ ...state,
+ panes: newPanes,
+ }
break;
case 'saveContent':
const panes = state.panes.map((pane)=>{
@@ -81,24 +99,59 @@ const consoleTabReducer = (state: any, action: any) => {
return newState;
}
-const ConsoleUI = ({clusterList}: any)=>{
+function calcHeightToPX(height: string){
+ const intHeight = parseInt(height)
+ if(height.endsWith('vh')){
+ return Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0) * intHeight / 100;
+ }else{
+ return intHeight;
+ }
+}
+
+export const ConsoleUI = ({selectedCluster,
+ clusterList,
+ clusterStatus,
+ minimize=false,
+ onMinimizeClick,
+ resizeable=false,
+ height='50vh'
+}: any)=>{
const clusterMap = useMemo(()=>{
let cm = {};
+ if(!clusterStatus){
+ return cm;
+ }
(clusterList || []).map((cluster: any)=>{
- cm[cluster.id] = cluster;
+ cluster.status = clusterStatus[cluster.id].health?.status;
+ if(!clusterStatus[cluster.id].available){
+ cluster.status = 'unavailable';
+ }
+ cm[cluster.id] = cluster;
});
return cm;
- }, [clusterList])
- const [localState, setLocalState, removeLocalState] = useLocalStorage("console:state", {
- panes: [],
- activeKey: '',
- },{
+ }, [clusterList, clusterStatus])
+ const initialDefaultState = ()=>{
+ const defaultActiveKey = `${selectedCluster.id}:${new Date().valueOf()}`;
+ const defaultState = selectedCluster? {
+ panes:[{
+ key: defaultActiveKey, cluster_id: selectedCluster.id, title: selectedCluster.name
+ }],
+ activeKey: defaultActiveKey,
+ }: {panes:[],activeKey:''};
+ return defaultState
+ }
+
+ const [localState, setLocalState, removeLocalState] = useLocalStorage("console:state", initialDefaultState, {
encode: JSON.stringify,
decode: JSON.parse,
})
const [tabState, dispatch] = useReducer(consoleTabReducer, localState)
useEffect(()=>{
+ if(tabState.panes.length == 0){
+ removeLocalState()
+ return
+ }
setLocalState(tabState)
}, [tabState])
@@ -150,20 +203,96 @@ const ConsoleUI = ({clusterList}: any)=>{
})}
);
+
+ const rootRef = useRef(null);
+ const [isFullscreen, setIsFullscreen] = useState(false);
+ const fullscreenClick = ()=>{
+ if(rootRef.current != null){
+ if(!isFullscreen){
+ rootRef.current.className = rootRef.current.className + " fullscreen";
+ // rootRef.current.style.overflow = 'scroll';
+ }else{
+ rootRef.current.className = rootRef.current.className.replace(' fullscreen', '');
+ }
+ }
+ setEditorHeight(rootRef.current.clientHeight)
+ setIsFullscreen(!isFullscreen)
+ }
- const tabBarExtra =(
-