console tab v0.1

This commit is contained in:
liugq 2021-10-15 11:41:38 +08:00
parent e1cd6d0cf7
commit b8fb6a3d87
21 changed files with 691 additions and 318 deletions

View File

@ -61,13 +61,28 @@ func (handler APIHandler) HandleUpdateDocumentAction(w http.ResponseWriter, req
err := handler.DecodeJSON(req, &reqBody)
if err != nil {
resBody["error"] = err
resBody["error"] = err.Error()
handler.WriteJSON(w, resBody, http.StatusOK)
return
}
indexName := ps.ByName("index")
docID := ps.ByName("docId")
typ := handler.GetParameter(req, "_type")
isNew := handler.GetParameter(req, "is_new")
if isNew == "1" {
getRes, err := client.Get(indexName, typ, docID)
if err != nil {
resBody["error"] = err.Error()
handler.WriteJSON(w, resBody, http.StatusOK)
return
}
if getRes.Found {
resBody["error"] = "doc id already exists"
handler.WriteJSON(w, resBody, http.StatusOK)
return
}
}
insertRes, err := client.Index(indexName, typ, docID, reqBody)
if err != nil {
resBody["error"] = err.Error()
@ -156,6 +171,30 @@ func (handler APIHandler) HandleSearchDocumentAction(w http.ResponseWriter, req
handler.WriteJSON(w, resResult, http.StatusOK)
}
func (handler APIHandler) ValidateDocIDAction(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
targetClusterID := ps.ByName("id")
client := elastic.GetClient(targetClusterID)
resBody := util.MapStr{}
if client != nil {
resBody["error"] = "cluster not found"
handler.WriteJSON(w, resBody, http.StatusOK)
return
}
var (
index = handler.GetParameter(req, "index")
docID = handler.GetParameter(req, "doc_id")
typ = handler.GetParameter(req, "type")
)
getRes, err := client.Get(index, typ, docID)
if err != nil {
resBody["error"] = err
handler.WriteJSON(w, resBody, http.StatusOK)
return
}
resBody["found"] = getRes.Found
handler.WriteJSON(w, resBody, http.StatusOK)
}
func formatESSearchResult(esResp *elastic.SearchResponse) map[string]interface{} {
total := esResp.Hits.Total
if len(esResp.Hits.Hits) == 0 {

View File

@ -5,6 +5,7 @@ import (
"infini.sh/framework/core/elastic"
"infini.sh/framework/core/util"
"net/http"
"runtime/debug"
)
func (handler APIHandler) HandleGetMappingsAction(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
@ -62,6 +63,11 @@ func (handler APIHandler) HandleGetSettingsAction(w http.ResponseWriter, req *ht
}
func (handler APIHandler) HandleUpdateSettingsAction(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
defer func() {
if err := recover(); err != nil {
debug.PrintStack()
}
}()
targetClusterID := ps.ByName("id")
client := elastic.GetClient(targetClusterID)
indexName := ps.ByName("index")

View File

@ -86,7 +86,6 @@ func reindex(esName string, body *model.Reindex, typ string) (string, error) {
func newResponseBody() map[string]interface{} {
return map[string]interface{}{
"status": true,
}
}

View File

@ -29,6 +29,7 @@ func Init(cfg *config.AppConfig) {
ui.HandleUIMethod(api.POST, path.Join(esPrefix, "doc/:index"), handler.HandleAddDocumentAction)
ui.HandleUIMethod(api.PUT, path.Join(esPrefix, "doc/:index/:docId"), handler.HandleUpdateDocumentAction)
ui.HandleUIMethod(api.DELETE, path.Join(esPrefix, "doc/:index/:docId"), handler.HandleDeleteDocumentAction)
ui.HandleUIMethod(api.GET, path.Join(esPrefix, "doc/_validate"), handler.ValidateDocIDAction)
ui.HandleUIMethod(api.POST, path.Join(pathPrefix, "rebuild/*id"), handler.HandleReindexAction)
ui.HandleUIMethod(api.GET, path.Join(pathPrefix, "rebuild/_search"), handler.HandleGetRebuildListAction)

View File

@ -162,7 +162,8 @@ export default [
// routes:[
// { path: '/', redirect: '/' },
// ],
// }, {
// },
// {
// path: '/data/template',
// name: 'template',
// component: './DataManagement/IndexTemplate',
@ -200,157 +201,161 @@ export default [
//search
// {
// path: '/search',
// name: 'search',
// icon: 'search',
// routes: [
// {
// path: '/search/overview',
// name: 'overview',
// component: './SearchManage/template/Template',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// {
// path: '/search/template',
// name: 'template',
// component: './SearchManage/template/Template',
// routes: [
// {
// path: '/search/template',
// redirect: '/search/template/template',
// },
// {
// path: '/search/template/template',
// component: './SearchManage/template/SearchTemplate',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// {
// path: '/search/template/:cluster_id',
// component: './SearchManage/template/SearchTemplate',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// {
// path: '/search/template/history',
// component: './SearchManage/template/History',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// ]
// }, {
// path: '/search/alias',
// name: 'alias',
// component: './SearchManage/alias/Alias',
// routes: [
// {
// path: '/search/alias',
// redirect: '/search/alias/index',
// // routes:[
// // { path: '/', redirect: '/' },
// // ],
// },
// {
// path: '/search/alias/index',
// component: './SearchManage/alias/AliasManage',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// {
// path: '/search/alias/rule',
// component: './SearchManage/alias/Rule',
// routes:[
// { path: '/', redirect: '/' },
// ],
// }
// ]
// }, {
// path: '/search/dict',
// name: 'dict',
// component: './SearchManage/dict/Dict',
// routes: [
// {
// path: '/search/dict',
// redirect: '/search/dict/professional',
// // routes:[
// // { path: '/', redirect: '/' },
// // ],
// },
// {
// path: '/search/dict/professional',
// component: './SearchManage/dict/Pro',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// {
// path: '/search/dict/common',
// component: './SearchManage/dict/Common',
// routes:[
// { path: '/', redirect: '/' },
// ],
// }
// ]
// }, {
// path: '/search/analyzer',
// name: 'analyzer',
// component: './SearchManage/analyzer/Analyzer',
// routes: [
// {
// path: '/search/analyzer',
// redirect: '/search/analyzer/manage',
// },
// {
// path: '/search/analyzer/manage',
// component: './SearchManage/analyzer/Manage',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// {
// path: '/search/analyzer/test',
// component: './SearchManage/analyzer/AnalyzerTest',
// routes:[
// { path: '/', redirect: '/' },
// ],
// }
// ]
// }//, {
// // path: '/search/nlp',
// // name: 'nlp',
// // component: './SearchManage/nlp/NLP',
// // routes: [
// // {
// // path: '/search/nlp',
// // redirect: '/search/nlp/query',
// // },
// // {
// // path: '/search/nlp/query',
// // component: './SearchManage/nlp/Query',
// // },
// // {
// // path: '/search/nlp/intention',
// // component: './SearchManage/nlp/Intention',
// // },
// // {
// // path: '/search/nlp/knowledge',
// // component: './SearchManage/nlp/Knowledge',
// // },
// // {
// // path: '/search/nlp/text',
// // component: './SearchManage/nlp/Text',
// // }
// //]
// //},
// ]
// },
{
path: '/search',
name: 'search',
icon: 'search',
routes: [
// {
// path: '/search/overview',
// name: 'overview',
// component: './SearchManage/template/Template',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// {
// path: '/search/template',
// name: 'template',
// component: './SearchManage/template/Template',
// routes: [
// {
// path: '/search/template',
// redirect: '/search/template/template',
// },
// {
// path: '/search/template/template',
// component: './SearchManage/template/SearchTemplate',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// {
// path: '/search/template/:cluster_id',
// component: './SearchManage/template/SearchTemplate',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// {
// path: '/search/template/history',
// component: './SearchManage/template/History',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// ]
// },
{
path: '/search/alias',
name: 'alias',
component: './SearchManage/alias/Alias',
routes: [
{
path: '/search/alias',
redirect: '/search/alias/index',
// routes:[
// { path: '/', redirect: '/' },
// ],
},
{
path: '/search/alias/index',
component: './SearchManage/alias/AliasManage',
routes:[
{ path: '/', redirect: '/' },
],
},
{
path: '/search/alias/rule',
component: './SearchManage/alias/Rule',
routes:[
{ path: '/', redirect: '/' },
],
}
]
},
// {
// path: '/search/dict',
// name: 'dict',
// component: './SearchManage/dict/Dict',
// routes: [
// {
// path: '/search/dict',
// redirect: '/search/dict/professional',
// // routes:[
// // { path: '/', redirect: '/' },
// // ],
// },
// {
// path: '/search/dict/professional',
// component: './SearchManage/dict/Pro',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// {
// path: '/search/dict/common',
// component: './SearchManage/dict/Common',
// routes:[
// { path: '/', redirect: '/' },
// ],
// }
// ]
// },
// {
// path: '/search/analyzer',
// name: 'analyzer',
// component: './SearchManage/analyzer/Analyzer',
// routes: [
// {
// path: '/search/analyzer',
// redirect: '/search/analyzer/manage',
// },
// {
// path: '/search/analyzer/manage',
// component: './SearchManage/analyzer/Manage',
// routes:[
// { path: '/', redirect: '/' },
// ],
// },
// {
// path: '/search/analyzer/test',
// component: './SearchManage/analyzer/AnalyzerTest',
// routes:[
// { path: '/', redirect: '/' },
// ],
// }
// ]
// }
//, {
// path: '/search/nlp',
// name: 'nlp',
// component: './SearchManage/nlp/NLP',
// routes: [
// {
// path: '/search/nlp',
// redirect: '/search/nlp/query',
// },
// {
// path: '/search/nlp/query',
// component: './SearchManage/nlp/Query',
// },
// {
// path: '/search/nlp/intention',
// component: './SearchManage/nlp/Intention',
// },
// {
// path: '/search/nlp/knowledge',
// component: './SearchManage/nlp/Knowledge',
// },
// {
// path: '/search/nlp/text',
// component: './SearchManage/nlp/Text',
// }
//]
//},
]
},
//
// //sync
// {

View File

@ -1,5 +1,5 @@
// @ts-ignore
import React, { useRef, useMemo } from 'react';
import React, { useRef, useMemo,useEffect } from 'react';
import ConsoleInput from './ConsoleInput';
import ConsoleOutput from './ConsoleOutput';
import { Panel } from './Panel';
@ -17,13 +17,19 @@ import {RequestStatusBar} from './request_status_bar';
interface props {
selectedCluster: any;
saveEditorContent: (content: string)=>void;
initialText: string;
paneKey: string;
}
const INITIAL_PANEL_WIDTH = 50;
const PANEL_MIN_WIDTH = '300px';
const ConsoleWrapper = ({
selectedCluster
selectedCluster,
saveEditorContent,
initialText,
paneKey,
}:props) => {
const {
@ -32,42 +38,74 @@ const ConsoleWrapper = ({
} = useRequestReadContext();
const lastDatum = requestData?.[requestData.length - 1] ?? requestError;
const getElementTop = (elem: any)=>{
  var elemTop=elem.offsetTop;
  elem=elem.offsetParent;
  while(elem!=null){
    elemTop+=elem.offsetTop;
    elem=elem.offsetParent;
  }
  return elemTop;
}
const statusBarRef = useRef<HTMLDivElement>(null);
const consoleRef = useRef<HTMLDivElement>(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)
}
},[])
return (
<div>
<EuiFlexGroup className="consoleContainer"
style={{height:30, background:'#fff'}}
gutterSize="none"
direction="column">
<EuiFlexItem className="conApp__tabsExtension">
<RequestStatusBar
requestInProgress={requestInProgress}
selectedCluster={selectedCluster}
requestResult={
lastDatum
? {
method: lastDatum.request.method.toUpperCase(),
endpoint: lastDatum.request.path,
statusCode: lastDatum.response.statusCode,
statusText: lastDatum.response.statusText,
timeElapsedMs: lastDatum.response.timeMs,
}
: undefined
}
/>
</EuiFlexItem>
</EuiFlexGroup>
<div className="Console">
<div ref={consoleRef} className="Console">
<PanelsContainer resizerClassName="resizer">
<Panel style={{ height: '100%', position: 'relative', minWidth: PANEL_MIN_WIDTH }} initialWidth={INITIAL_PANEL_WIDTH}>
<ConsoleInput clusterID={selectedCluster.id} />
<ConsoleInput clusterID={selectedCluster.id} saveEditorContent={saveEditorContent} initialText={initialText} paneKey={paneKey} />
</Panel>
<Panel style={{ height: '100%', position: 'relative', minWidth: PANEL_MIN_WIDTH }} initialWidth={INITIAL_PANEL_WIDTH}>
<ConsoleOutput clusterID={selectedCluster.id} />
</Panel>
</PanelsContainer>
</div>
<div ref={statusBarRef} style={{ position:'fixed', bottom:0, borderTop: '1px solid #eee', zIndex:5000}}>
<EuiFlexGroup className="consoleContainer"
style={{height:30, background:'#fff'}}
gutterSize="none"
direction="column">
<EuiFlexItem className="conApp__tabsExtension">
<RequestStatusBar
requestInProgress={requestInProgress}
selectedCluster={selectedCluster}
requestResult={
lastDatum
? {
method: lastDatum.request.method.toUpperCase(),
endpoint: lastDatum.request.path,
statusCode: lastDatum.response.statusCode,
statusText: lastDatum.response.statusText,
timeElapsedMs: lastDatum.response.timeMs,
}
: undefined
}
/>
</EuiFlexItem>
</EuiFlexGroup>
</div>
</div>
);
};

View File

@ -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 } from '../contexts/editor_context/editor_registry';
import { instance as registry, editorList } 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';
@ -66,6 +66,8 @@ const SendRequestButton = (props: any) => {
interface ConsoleInputProps {
clusterID: string,
initialText: string | undefined,
saveEditorContent: (content: string)=>void,
paneKey: string,
}
const DEFAULT_INPUT_VALUE = `GET _search
@ -75,7 +77,8 @@ const DEFAULT_INPUT_VALUE = `GET _search
}
}`;
const ConsoleInputUI = ({clusterID, initialText}:ConsoleInputProps) => {
const ConsoleInputUI = ({clusterID, initialText, saveEditorContent, paneKey}:ConsoleInputProps) => {
const editorRef = useRef<HTMLDivElement | null>(null);
const editorActionsRef = useRef<HTMLDivElement | null>(null);
const editorInstanceRef = useRef<SenseEditor | null>(null);
@ -105,8 +108,10 @@ const ConsoleInputUI = ({clusterID, initialText}:ConsoleInputProps) => {
// senseEditor.highlightCurrentRequestsAndUpdateActionBar();
editorInstanceRef.current = senseEditor;
setInputEditor(senseEditor);
senseEditor.paneKey = paneKey;
editorList.addInputEditor(senseEditor);
senseEditor.update(initialText || DEFAULT_INPUT_VALUE);
// applyCurrentSettings(senseEditor!.getCoreEditor(), {fontSize:14, wrapMode: true,});
applyCurrentSettings(senseEditor!.getCoreEditor(), {fontSize:12, wrapMode: true,});
function setupAutosave() {
let timer: number;
@ -123,7 +128,8 @@ const ConsoleInputUI = ({clusterID, initialText}:ConsoleInputProps) => {
function saveCurrentState() {
try {
const content = senseEditor.getCoreEditor().getValue();
saveCurrentTextObjectRef.current(content);
// saveCurrentTextObjectRef.current(content);
saveEditorContent(content)
} catch (e) {
console.log(e)
// Ignoring saving error
@ -142,6 +148,7 @@ const ConsoleInputUI = ({clusterID, initialText}:ConsoleInputProps) => {
}, []);
useEffect(()=>{
retrieveAutoCompleteInfo(settings, settings.getAutocomplete(), clusterID);
aceEditorRef.current && (aceEditorRef.current['clusterID'] = clusterID);
},[clusterID])
const handleSaveAsCommonCommand = async () => {
@ -188,7 +195,7 @@ const ConsoleInputUI = ({clusterID, initialText}:ConsoleInputProps) => {
</EuiFlexGroup>
<div
ref={editorRef}
id="ConAppEditor"
id={`Editor_${editorInstanceRef.current?.paneKey}`}
className="conApp__editorContent"
data-test-subj="request-editor"
onClick={()=>{consoleMenuRef.current?.closePopover(); aceEditorRef.current?.focus()}}
@ -198,13 +205,14 @@ const ConsoleInputUI = ({clusterID, initialText}:ConsoleInputProps) => {
);
};
const ConsoleInput = ({clusterID}:{clusterID:string})=>{
const { done, error, retry } = useDataInit();
const { currentTextObject } = useEditorReadContext();
return done ? <ConsoleInputUI clusterID={clusterID} initialText={currentTextObject?.text}/>: <></>
}
// const ConsoleInput = ({clusterID}:{clusterID:string})=>{
// const { done, error, retry } = useDataInit();
// const { currentTextObject } = useEditorReadContext();
// return done ? <ConsoleInputUI clusterID={clusterID} initialText={currentTextObject?.text}/>: <></>
// }
export default ConsoleInput;
// export default ConsoleInput;
export default ConsoleInputUI;

View File

@ -46,7 +46,7 @@ function ConsoleOutput({clusterID}: props) {
const textarea = editorRef.current!.querySelector('textarea')!;
textarea.setAttribute('id', inputId);
textarea.setAttribute('readonly', 'true');
// applyCurrentSettings(editorInstanceRef.current!, {fontSize:14, wrapMode: true,})
applyCurrentSettings(editorInstanceRef.current!, {fontSize:12, wrapMode: true,})
const unsubscribeResizer = subscribeResizeChecker(editorRef.current!, editorInstanceRef.current!);
return () => {

View File

@ -37,6 +37,7 @@ export class EditorRegistry {
setInputEditor(inputEditor: SenseEditor) {
this.inputEditor = inputEditor;
inputEditor.setAutocompleter();
}
getInputEditor() {
@ -46,3 +47,29 @@ export class EditorRegistry {
// Create a single instance of this and use as private state.
export const instance = new EditorRegistry();
export class EditorList {
private inputEditors: SenseEditor[];
constructor(){
this.inputEditors = [];
}
addInputEditor(inputEditor: SenseEditor) {
this.inputEditors.push(inputEditor);
}
removeInputEditor(inputEditor: SenseEditor){
const idx = this.inputEditors.findIndex(editor=> editor==inputEditor);
if(idx > -1) this.inputEditors = this.inputEditors.splice(idx,1);
}
setActiveEditor(key: string){
for(let i=0; i<this.inputEditors.length; i++){
const editor = this.inputEditors[i];
if(key == editor.paneKey) {
instance.setInputEditor(editor);
break;
}
}
}
}
export const editorList = new EditorList();

View File

@ -30,6 +30,10 @@ 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;

View File

@ -333,6 +333,30 @@ export function getCurrentMethodAndTokenPaths(
return ret;
}
function isUrlPathToken(token: Token | null) {
switch ((token || ({} as Token)).type) {
case 'url.slash':
case 'url.comma':
case 'url.part':
return true;
default:
return false;
}
}
function addMetaToTermsList(
list: unknown[],
meta: unknown,
template?: string
): Array<{ meta: unknown; template: unknown; name?: string }> {
return _.map(list, function (t) {
if (typeof t !== 'object') {
t = { name: t };
}
return _.defaults(t, { meta, template });
});
}
// eslint-disable-next-line
export default function ({
coreEditor: editor,
@ -341,29 +365,7 @@ export default function ({
coreEditor: CoreEditor;
parser: RowParser;
}) {
function isUrlPathToken(token: Token | null) {
switch ((token || ({} as Token)).type) {
case 'url.slash':
case 'url.comma':
case 'url.part':
return true;
default:
return false;
}
}
function addMetaToTermsList(
list: unknown[],
meta: unknown,
template?: string
): Array<{ meta: unknown; template: unknown; name?: string }> {
return _.map(list, function (t) {
if (typeof t !== 'object') {
t = { name: t };
}
return _.defaults(t, { meta, template });
});
}
function applyTerm(term: {
value?: string;

View File

@ -10,6 +10,28 @@ 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, []);
};
}
);
const langTools = ace.acequire('ace/ext/language_tools');
// @ts-ignore
import * as InputMode from './mode/input';
@ -351,28 +373,6 @@ export class LegacyCoreEditor implements CoreEditor {
// disable standard context based autocompletion.
// @ts-ignore
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
[{

View File

@ -40,14 +40,15 @@ const POLL_INTERVAL = 60000;
let pollTimeoutId;
let perIndexTypes = {};
let perAliasIndexes = [];
let templates = [];
let perAliasIndexes = {};
let templates = {};
//new add
let commands = [];
const mappingObj = {};
let clusterID = '';
export function expandAliases(indicesOrAliases) {
export function expandAliases(indicesOrAliases, clusterID) {
const clusterPerAliasIndexes = perAliasIndexes[clusterID] || {};
// takes a list of indices or aliases or a string which may be either and returns a list of indices
// returns a list for multiple values or a string for a single.
@ -59,8 +60,8 @@ export function expandAliases(indicesOrAliases) {
indicesOrAliases = [indicesOrAliases];
}
indicesOrAliases = $.map(indicesOrAliases, function (iOrA) {
if (perAliasIndexes[iOrA]) {
return perAliasIndexes[iOrA];
if (clusterPerAliasIndexes[iOrA]) {
return clusterPerAliasIndexes[iOrA];
}
return [iOrA];
});
@ -75,17 +76,21 @@ export function expandAliases(indicesOrAliases) {
return ret.length > 1 ? ret : ret[0];
}
export function getTemplates() {
return [...templates];
export function getTemplates(key) {
key = key.editor.editor.clusterID || clusterID;
const clusterTemplates = templates[key] || [];
return [...clusterTemplates];
}
export function getFields(indices, types) {
export function getFields(indices, types, key) {
key = key || clusterID
const clusterPerIndexTypes = perIndexTypes[key] || {};
// get fields for indices and types. Both can be a list, a string or null (meaning all).
let ret = [];
indices = expandAliases(indices);
indices = expandAliases(indices, key);
if (typeof indices === 'string') {
const typeDict = perIndexTypes[indices];
const typeDict = clusterPerIndexTypes[indices];
if (!typeDict) {
return [];
}
@ -105,9 +110,9 @@ export function getFields(indices, types) {
}
} else {
// multi index mode.
$.each(perIndexTypes, function (index) {
$.each(clusterPerIndexTypes, function (index) {
if (!indices || indices.length === 0 || $.inArray(index, indices) !== -1) {
ret.push(getFields(index, types));
ret.push(getFields(index, types, key));
}
});
ret = [].concat.apply([], ret);
@ -118,11 +123,12 @@ export function getFields(indices, types) {
});
}
export function getTypes(indices) {
export function getTypes(indices, clusterID) {
const clusterPerIndexTypes = perIndexTypes[clusterID] || {};
let ret = [];
indices = expandAliases(indices);
indices = expandAliases(indices, clusterID);
if (typeof indices === 'string') {
const typeDict = perIndexTypes[indices];
const typeDict = clusterPerIndexTypes[indices];
if (!typeDict) {
return [];
}
@ -133,9 +139,9 @@ export function getTypes(indices) {
});
} else {
// multi index mode.
$.each(perIndexTypes, function (index) {
$.each(clusterPerIndexTypes, function (index) {
if (!indices || $.inArray(index, indices) !== -1) {
ret.push(getTypes(index));
ret.push(getTypes(index, clusterID));
}
});
ret = [].concat.apply([], ret);
@ -144,13 +150,18 @@ export function getTypes(indices) {
return _.uniq(ret);
}
export function getIndices(includeAliases) {
export function getIndices(includeAliases, key) {
if(typeof key != 'string') {
key = key?.editor?.clusterID || clusterID
}
const clusterPerIndexTypes = perIndexTypes[key] || {};
const clusterPerAliasIndexes = perAliasIndexes[key] || [];
const ret = [];
$.each(perIndexTypes, function (index) {
$.each(clusterPerIndexTypes, function (index) {
ret.push(index);
});
if (typeof includeAliases === 'undefined' ? true : includeAliases) {
$.each(perAliasIndexes, function (alias) {
$.each(clusterPerAliasIndexes, function (alias) {
ret.push(alias);
});
}
@ -211,12 +222,12 @@ function getFieldNamesFromProperties(properties = {}) {
});
}
function loadTemplates(templatesObject = {}) {
templates = Object.keys(templatesObject);
function loadTemplates(templatesObject = {}, clusterID) {
templates[clusterID] = Object.keys(templatesObject);
}
export function loadMappings(mappings) {
perIndexTypes = {};
export function loadMappings(mappings, clusterID) {
let clusterPerIndexTypes = {};
$.each(mappings, function (index, indexMapping) {
const normalizedIndexMappings = {};
@ -234,31 +245,32 @@ export function loadMappings(mappings) {
normalizedIndexMappings[typeName] = [];
}
});
perIndexTypes[index] = normalizedIndexMappings;
clusterPerIndexTypes[index] = normalizedIndexMappings;
});
perIndexTypes[clusterID] = clusterPerIndexTypes;
}
export function loadAliases(aliases) {
perAliasIndexes = {};
export function loadAliases(aliases, clusterID) {
let clusterPerAliasIndexes = {};
$.each(aliases || {}, function (index, omdexAliases) {
// verify we have an index defined. useful when mapping loading is disabled
perIndexTypes[index] = perIndexTypes[index] || {};
// clusterPerAliasIndexes[index] = clusterPerAliasIndexes[index] || {};
$.each(omdexAliases.aliases || {}, function (alias) {
if (alias === index) {
return;
} // alias which is identical to index means no index.
let curAliases = perAliasIndexes[alias];
let curAliases = clusterPerAliasIndexes[alias];
if (!curAliases) {
curAliases = [];
perAliasIndexes[alias] = curAliases;
clusterPerAliasIndexes[alias] = curAliases;
}
curAliases.push(index);
});
});
perAliasIndexes._all = getIndices(false);
clusterPerAliasIndexes._all = getIndices(false, clusterID);
perAliasIndexes[clusterID] = clusterPerAliasIndexes;
}
export function clear() {
@ -343,15 +355,15 @@ export function retrieveAutoCompleteInfo(settings, settingsToRetrieve, clusterID
} else {
mappingsResponse = mappings[0];
}
loadMappings(getObject(mappingsResponse)); //
loadMappings(getObject(mappingsResponse), clusterID); //
}
if (aliases) {
loadAliases(getObject(aliases[0]));
loadAliases(getObject(aliases[0]), clusterID);
}
if (templates) {
loadTemplates(getObject(templates[0]));
loadTemplates(getObject(templates[0]), clusterID);
}
if (mappings && aliases) {
@ -410,4 +422,8 @@ export function getCommand(title) {
return c._source['title'] == title;
})
return command && command[0];
}
export function setClusterID(id) {
clusterID = id;
}

View File

@ -63,7 +63,7 @@ const Table: React.FC<TableProps> = ({ columns, hits, sortOrder, indexPattern, o
<tbody>
{hits.slice(0, scrollState.limit).map((row, idx)=>{
return <TableRow key={'discover-table-row'+idx} onFilter={onFilter}
return <TableRow key={'discover-table-row'+row._id} onFilter={onFilter}
columns={columns}
hideTimeColumn={false}
indexPattern={indexPattern}

View File

@ -51,12 +51,15 @@ export function Detail({
message.error('wrong json format')
return
}
const res = await document.saveDocument({
let params = {
_index: row._index,
_id: docID || row._id,
_type: row._type,
_source: source
})
_source: source,
};
docID && (params['is_new'] = '1')
const res = await document.saveDocument(params)
if(!res.error) setEditorVisble(false)
}
const deleteDocumentClick = ()=>{

View File

@ -83,7 +83,7 @@ const indexPatternManagementStart = indexPatternManagementService.start()
const indexPatternsApiClient = new IndexPatternsApiClient(http);
const uiconfigs = {
['metaFields']: ['_source', '_id', '_type', '_index', '_score'],
['metaFields']: ['_source', '_id', '_type', '_index' ],//'_score'
defaultIndex: '',
};
const uiSettings = {

View File

@ -0,0 +1,40 @@
import { useCallback, useState, useEffect } from "react"
export function useLocalStorage(key, defaultValue, encryptor) {
return useStorage(key, defaultValue, window.localStorage, encryptor)
}
export function useSessionStorage(key, defaultValue) {
return useStorage(key, defaultValue, window.sessionStorage)
}
function useStorage(key, defaultValue, storageObject, encryptor) {
const [value, setValue] = useState(() => {
const storeValue = storageObject.getItem(key)
if (storeValue) {
if(encryptor && typeof encryptor.encode == 'function') return encryptor.decode(storeValue);
return storeValue
}
if (typeof initialValue === "function") {
return defaultValue()
} else {
return defaultValue
}
})
useEffect(() => {
if (value === undefined) return storageObject.removeItem(key)
let toStoreValue = value;
if(encryptor && typeof encryptor.encode == 'function'){
toStoreValue = encryptor.encode(toStoreValue)
}
storageObject.setItem(key, toStoreValue)
}, [key, value, storageObject])
const remove = useCallback(() => {
setValue(undefined)
}, [])
return [value, setValue, remove]
}

View File

@ -93,7 +93,6 @@ const Discover = (props)=>{
columns: ['_source'],
sort: [],
})
// updateQuery({indexPattern:IP});
}
//const indexPatterns = [{"id":"1ccce5c0-bb9a-11eb-957b-939add21a246","type":"index-pattern","namespaces":["default"],"updated_at":"2021-05-23T07:40:14.747Z","version":"WzkxOTEsNDhd","attributes":{"title":"test-custom*","timeFieldName":"created_at","fields":"[{\"count\":0,\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"count\":0,\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"count\":0,\"name\":\"_score\",\"type\":\"number\",\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"count\":0,\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"count\":0,\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"count\":0,\"name\":\"address\",\"type\":\"string\",\"esTypes\":[\"text\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"count\":0,\"name\":\"address.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"address\"}}},{\"count\":0,\"conflictDescriptions\":{\"text\":[\"test-custom1\"],\"long\":[\"test-custom\",\"test-custom8\",\"test-custom9\"]},\"name\":\"age\",\"type\":\"conflict\",\"esTypes\":[\"text\",\"long\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"count\":0,\"name\":\"age.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"age\"}}},{\"count\":0,\"name\":\"created_at\",\"type\":\"date\",\"esTypes\":[\"date\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"count\":0,\"name\":\"email\",\"type\":\"string\",\"esTypes\":[\"text\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"count\":0,\"name\":\"email.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"email\"}}},{\"count\":0,\"name\":\"hobbies\",\"type\":\"string\",\"esTypes\":[\"text\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"count\":0,\"name\":\"hobbies.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"hobbies\"}}},{\"count\":0,\"name\":\"id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"count\":0,\"name\":\"id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"id\"}}},{\"count\":0,\"name\":\"name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"count\":0,\"name\":\"name.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"name\"}}}]"},"references":[],"migrationVersion":{"index-pattern":"7.6.0"}}];
@ -113,9 +112,7 @@ const Discover = (props)=>{
const columns = state.columns;
const updateQuery = useCallback(
async (_payload, isUpdate) => {
// if (isUpdate === false) {
// }
async (_payload) => {
if(!indexPattern){
return
}
@ -139,11 +136,9 @@ const Discover = (props)=>{
//console.log(JSON.stringify(params));
//console.log(getEsQuery(indexPattern))
},
[props.indexPatternList, state.interval, state.sort]
[indexPattern, state.interval,]
);
const onChangeInterval = useCallback(
(interval) => {
if (interval) {
@ -276,7 +271,7 @@ const Discover = (props)=>{
filterManager.addFilters(newFilters);
updateQuery()
},
[indexPattern, indexPatterns]
[indexPattern]
);
const timefilterUpdateHandler = useCallback(
@ -308,12 +303,13 @@ const Discover = (props)=>{
const resetQuery = ()=>{};
const showDatePicker = indexPattern.timeFieldName != "";
const saveDocument = useCallback(async ({_index, _id, _type, _source})=>{
const saveDocument = useCallback(async ({_index, _id, _type, _source, is_new})=>{
const {http} = getContext();
const res = await http.put(`/elasticsearch/${props.selectedCluster.id}/doc/${_index}/${_id}`, {
prependBasePath: false,
query: {
_type,
is_new,
},
body: JSON.stringify(_source),
});
@ -323,8 +319,8 @@ const Discover = (props)=>{
}
message.success('saved successfully');
updateQuery()
return res
},[props.selectedCluster])
return res;
},[props.selectedCluster, updateQuery])
const deleteDocument = useCallback(async ({_index, _id, _type})=>{
const {http} = getContext();
@ -341,7 +337,7 @@ const Discover = (props)=>{
message.success('deleted successfully');
updateQuery()
return res
},[props.selectedCluster])
},[props.selectedCluster, updateQuery])
return (
<Card bordered={false}>
@ -589,9 +585,9 @@ const DiscoverUI = (props)=>{
}
}
initialFetch();
return ()=>{
queryStringManager.setQuery('');
}
// return ()=>{
// queryStringManager.setQuery('');
// }
},[props.selectedCluster]);
function changeIndexPattern(indexPattern){

View File

@ -61,13 +61,18 @@ class JSONWrapper extends PureComponent {
class CreateForm extends React.Component {
okHandle = () => {
const {handleAdd, form} = this.props;
const me = this;
form.validateFields((err, fieldsValue) => {
if (err) return;
form.resetFields();
fieldsValue['config'] = this.editorValueGetter();
fieldsValue['config'] = me.editor.getValue();
handleAdd(fieldsValue);
form.resetFields();
});
};
onEditorDidMount = (editor)=>{
this.editor = editor;
}
render() {
const {modalVisible, form, handleModalVisible} = this.props;
@ -98,7 +103,7 @@ class CreateForm extends React.Component {
tabSize: 2,
wordBasedSuggestions: true,
}}
editorDidMount={(valueGetter)=>{this.editorValueGetter=valueGetter}}
onMount={this.onEditorDidMount}
/>
</div>
</FormItem>
@ -162,8 +167,8 @@ class Index extends PureComponent {
<Popconfirm title="Sure to delete?" onConfirm={() => this.handleDeleteClick(record.index)}>
<a>删除</a>
</Popconfirm>
<Divider type="vertical" />
<Link to={"/data/document?index=" + record.index}>文档管理</Link>
{/* <Divider type="vertical" />
<Link to={"/data/document?index=" + record.index}>文档管理</Link> */}
</Fragment>
),
},
@ -268,12 +273,12 @@ class Index extends PureComponent {
})
}
}
handleEditorDidMount = (editorName, _valueGetter)=>{
this[editorName] = _valueGetter;
handleEditorDidMount = (editorName, editor)=>{
this[editorName] = editor;
}
handleIndexSettingsSaveClick = (indexName)=>{
let settings = this.indexSettingsGetter();
let settings = this.indexSettingsEditor.getValue();
settings = JSON.parse(settings);
const {dispatch,clusterID} = this.props;
dispatch({
@ -423,7 +428,7 @@ class Index extends PureComponent {
tabSize: 2,
wordBasedSuggestions: true,
}}
editorDidMount={(valueGetter)=>this.handleEditorDidMount('indexSettingsGetter', valueGetter)}
onMount={(editor)=>this.handleEditorDidMount('indexSettingsEditor', editor)}
/>
</div>
</TabPane>
@ -432,10 +437,15 @@ class Index extends PureComponent {
<Dropdown
placement="topLeft"
overlay={(
<Menu onClick={()=>{}}>
<Menu>
<Menu.Item key="1">
<Icon type="delete" />
Delete
<Popconfirm onConfirm={()=>{
this.handleDeleteClick(editingIndex.index);
this.setState({drawerVisible: false})
}} title="sure to delete ?">
<Icon type="delete" />
Delete
</Popconfirm>
</Menu.Item>
{/*<Menu.Item key="2">*/}
{/* <Icon type="edit" />*/}

View File

@ -13,7 +13,7 @@ export default {
*fetchIndices({payload}, {call, put}){
let resp = yield call(getIndices, payload)
if(resp.error){
message.warn("获取数据失败")
message.error("获取数据失败")
return
}
yield put({
@ -27,7 +27,7 @@ export default {
*fetchMappings({payload}, {call, put}){
let resp = yield call(getMappings, payload);
if(resp.error){
message.warn("get mappings failed")
message.warn(resp.error)
return
}
yield put({
@ -40,7 +40,7 @@ export default {
*fetchSettings({payload}, {call, put}){
let resp = yield call(getSettings, payload);
if(resp.error){
message.warn("get settings failed")
message.warn(resp.error)
return
}
yield put({
@ -53,9 +53,12 @@ export default {
*saveSettings({payload}, {call, put, select}){
let resp = yield call(updateSettings, payload);
if(resp.error){
message.warn("save settings failed")
message.error(resp.error)
return
}
if(resp.result == 'updated'){
message.success("save successfully")
}
let {settings} = yield select(state=>state.index);
settings[payload.index] = payload.settings;
yield put({
@ -68,7 +71,7 @@ export default {
*removeIndex({payload}, {call, put, select}){
let resp = yield call(deleteIndex, payload);
if(resp.error){
message.warn("get mappings failed")
message.error(resp.error)
return
}
let {clusterIndices} = yield select(state=>state.index);
@ -83,7 +86,7 @@ export default {
*addIndex({payload}, {call, put, select}){
let resp = yield call(createIndex, payload);
if(resp.error){
message.warn("create index failed")
message.error(resp.error)
return
}
yield put({

View File

@ -1,15 +1,191 @@
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 {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';
// export default ()=>{
// return (
// <Console />
// )
// }
const { TabPane } = Tabs;
const TabConsole = (props:any)=>{
return (
<Console {...props}/>
)
}
// export default connect(({
// global
// })=>({
// selectedCluster: global.selectedCluster,
// }))(Console);
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});
return {
...state,
panes,
activeKey,
}
}
const removeTab = (state: any, action: any) =>{
const { activeKey, panes } = state;
const {targetKey} = action.payload;
const newPanes = panes.filter(pane => pane.key !== targetKey);
return {
...state,
panes: newPanes,
activeKey: panes[0]?.key,
}
}
const consoleTabReducer = (state: any, action: any) => {
const {type, payload} = action;
let newState = state;
switch(type){
case 'add':
newState = addTab(state, action);
break;
case 'remove':
newState = removeTab(state, action);
break;
case 'change':
newState = {
...state,
activeKey: payload.activeKey,
}
editorList.setActiveEditor(payload.activeKey);
break;
case 'saveContent':
const panes = state.panes.map((pane)=>{
if(pane.key == state.activeKey){
return {
...pane,
content: action.payload.content,
}
}
return pane;
});
newState = ({
...state,
panes,
});
break;
default:
}
// setLocalState(newState);
return newState;
}
const ConsoleUI = ({clusterList}: any)=>{
const clusterMap = useMemo(()=>{
let cm = {};
(clusterList || []).map((cluster: any)=>{
cm[cluster.id] = cluster;
});
return cm;
}, [clusterList])
const [localState, setLocalState, removeLocalState] = useLocalStorage("console:state", {
panes: [],
activeKey: '',
},{
encode: JSON.stringify,
decode: JSON.parse,
})
const [tabState, dispatch] = useReducer(consoleTabReducer, localState)
useEffect(()=>{
setLocalState(tabState)
}, [tabState])
const saveEditorContent = useCallback((content)=>{
dispatch({
type: 'saveContent',
payload: {
content
}
})
}, [dispatch])
const onChange = (activeKey:string) => {
dispatch({
type: 'change',
payload: {
activeKey
}
})
};
const onEdit = (targetKey: string | React.MouseEvent<HTMLElement, MouseEvent>, action:string) => {
dispatch({
type: action,
payload: {
targetKey,
}
})
};
const newTabClick = useCallback((param: any)=>{
const cluster = clusterList.find(item=>item.id == param.key);
if(!cluster){
console.log('cluster not found')
return;
}
dispatch({
type:'add',
payload: {
cluster,
}
})
},[clusterList])
const menu = (
<Menu onClick={newTabClick}>
{(clusterList||[]).map((cluster:any)=>{
return <Menu.Item key={cluster.id}>{cluster.name}</Menu.Item>
})}
</Menu>
);
const tabBarExtra =(<Dropdown overlay={menu}>
<Button size="small">
<Icon type="plus"/>
</Button>
</Dropdown>);
setClusterID(tabState.activeKey?.split(':')[0]);
const panes = tabState.panes.filter((pane: any)=>{
return typeof clusterMap[pane.cluster_id] != 'undefined';
})
return (
<div style={{background:'#fff'}}>
<Tabs
onChange={onChange}
activeKey={tabState.activeKey}
type="editable-card"
onEdit={onEdit}
hideAdd
tabBarExtraContent={tabBarExtra}
>
{panes.map(pane => (
<TabPane tab={clusterMap[pane.cluster_id].name} key={pane.key} closable={pane.closable}>
<TabConsole selectedCluster={clusterMap[pane.cluster_id]} paneKey={pane.key} saveEditorContent={saveEditorContent} initialText={pane.content} />
{/* {pane.content} */}
</TabPane>
))}
</Tabs>
</div>
);
}
export default connect(({
global
})=>({
selectedCluster: global.selectedCluster,
}))(Console);
clusterList: global.clusterList,
}))(ConsoleUI);