fix: Updated QueryEditor component to support languages (#3988)

fix #4292 
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a new custom `QueryEditor` component for a more
customizable and consistent editing experience across various forms and
editors.
- Added support for a new programming language, VRL, including syntax
highlighting and theming.

- **Enhancements**
- Updated multiple components (`AddTemplate.vue`, `AddFunction.vue`,
`EditRole.vue`) to utilize the `QueryEditor` component, improving
editing functionality and user interface.
- Enhanced the `SearchBar.vue` component for better formatting and
language handling.

- **Bug Fixes**
- Adjusted the language attribute in the `SearchBar.vue` component from
"ruby" to "vrl" to ensure correct syntax highlighting.

- **Tests**
- Improved stability of the alerts test suite by adding a timeout after
user input actions.
- Added a timeout in the sanity test cases to enhance the reliability of
template submission.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Omkar Kesarkhane
2024-08-22 21:12:55 +05:30
committed by GitHub
parent d80a219ba2
commit 7a8656dfc4
9 changed files with 795 additions and 586 deletions
@@ -214,6 +214,7 @@ test.describe("Alerts testcases", () => {
'[data-test="add-template-name-input"]').fill("test");
await page.click(".view-line")
await page.keyboard.type('test');
await page.waitForTimeout(500);
await page.locator(
'[data-test="add-template-submit-btn"]').click({ force: true }); await expect(page.locator(
".q-notification__message").getByText(/Please enter valid JSON/).first()).toBeVisible();
@@ -427,6 +427,7 @@ test.describe("Sanity testcases", () => {
const jsonString = '{"text": "{alert_name} is active"}';
await page.click(".view-line");
await page.keyboard.type(jsonString);
await page.waitForTimeout(500);
await page
.locator('[data-test="add-template-submit-btn"]')
.click({ force: true });
+53 -3
View File
@@ -37,9 +37,8 @@ import {
} from "vue";
import "monaco-editor/esm/vs/editor/editor.all.js";
import "monaco-editor/esm/vs/basic-languages/sql/sql.contribution.js";
import "monaco-editor/esm/vs/basic-languages/sql/sql.js";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
import { vrlLanguageDefinition } from "@/utils/query/vrlLanguageDefinition";
import { useStore } from "vuex";
import { debounce } from "quasar";
@@ -125,7 +124,7 @@ export default defineComponent({
});
};
onMounted(async () => {
const setupEditor = () => {
monaco.editor.defineTheme("myCustomTheme", {
base: "vs", // can also be vs-dark or hc-black
inherit: true, // can also be false to completely replace the builtin rules
@@ -210,6 +209,52 @@ export default defineComponent({
window.addEventListener("click", () => {
editorObj.layout();
});
window.addEventListener("resize", async () => {
await nextTick();
editorObj.layout();
// queryEditorRef.value.resetEditorLayout();
});
};
onMounted(async () => {
provider.value?.dispose();
if (props.language === "vrl") {
monaco.languages.register({ id: "vrl" });
// Register a tokens provider for the language
monaco.languages.setMonarchTokensProvider(
"vrl",
vrlLanguageDefinition as any
);
}
if (props.language === "sql") {
await import(
"monaco-editor/esm/vs/basic-languages/sql/sql.contribution.js"
);
}
if (props.language === "json") {
await import(
"monaco-editor/esm/vs/language/json/monaco.contribution.js"
);
}
if (props.language === "html") {
await import(
"monaco-editor/esm/vs/language/html/monaco.contribution.js"
);
}
if (props.language === "markdown") {
await import(
"monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution.js"
);
}
setupEditor();
});
onActivated(async () => {
@@ -329,6 +374,10 @@ export default defineComponent({
editorRef.value.dispatchEvent(escEvent);
};
const formatDocument = () => {
editorObj.getAction("editor.action.formatDocument").run();
};
const getCursorIndex = () => {
const currentPosition = editorObj.getPosition();
const cursorIndex = editorObj.getModel().getOffsetAt(currentPosition) - 1;
@@ -344,6 +393,7 @@ export default defineComponent({
triggerAutoComplete,
getCursorIndex,
searchObj,
formatDocument,
};
},
});
+11 -83
View File
@@ -81,15 +81,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
>
{{ t("alert_templates.body") + " *" }}
</div>
<div
data-test="add-template-body-input"
<query-editor
editor-id="add-template-editor"
class="monaco-editor q-mt-sm"
language="json"
ref="editorRef"
id="editor"
v-model:query="formData.body"
data-test="add-template-body-input"
:label="t('alerts.sql')"
stack-label
style="border: 1px solid #dbdbdb; border-radius: 5px"
class="showLabelOnTop"
resize
:rules="[(val: any) => !!val || 'Field is required!']"
/>
</div>
@@ -169,29 +168,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</q-page>
</template>
<script lang="ts" setup>
import {
ref,
onMounted,
defineProps,
onBeforeMount,
onActivated,
defineEmits,
watch,
} from "vue";
import { ref, defineProps, onBeforeMount, onActivated, defineEmits } from "vue";
import type { Ref } from "vue";
import { useI18n } from "vue-i18n";
import "monaco-editor/esm/vs/editor/editor.all.js";
import "monaco-editor/esm/vs/language/json/monaco.contribution.js";
import "monaco-editor/esm/vs/language/json/jsonMode.js";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
import templateService from "@/services/alert_templates";
import { useStore } from "vuex";
import { copyToClipboard, useQuasar } from "quasar";
import type { TemplateData, Template } from "@/ts/interfaces/index";
import { useRouter } from "vue-router";
import { isValidResourceName } from "@/utils/zincutils";
import QueryEditor from "@/components/QueryEditor.vue";
const props = defineProps<{ template: TemplateData | null }>();
const emit = defineEmits(["get:templates", "cancel:hideform"]);
@@ -204,8 +191,6 @@ const formData: Ref<Template> = ref({
const store = useStore();
const q = useQuasar();
const editorRef: any = ref(null);
let editorobj: any = null;
const editorData = ref("");
const isUpdatingTemplate = ref(false);
const sampleTemplates = [
{
@@ -238,51 +223,6 @@ onActivated(() => setupTemplateData());
onBeforeMount(() => {
setupTemplateData();
});
onMounted(async () => {
monaco.editor.defineTheme("myCustomTheme", {
base: "vs", // can also be vs-dark or hc-black
inherit: true, // can also be false to completely replace the builtin rules
rules: [
{
token: "comment",
foreground: "ffa500",
background: "FFFFFF",
fontStyle: "italic underline",
},
{
token: "comment.js",
foreground: "008800",
fontStyle: "bold",
background: "FFFFFF",
},
{ token: "comment.css", foreground: "0000ff", background: "FFFFFF" }, // will inherit fontStyle from `comment` above
],
colors: {
"editor.foreground": "#000000",
"editor.background": "#FFFFFF",
"editorCursor.foreground": "#000000",
"editor.lineHighlightBackground": "#FFFFFF",
"editorLineNumber.foreground": "#000000",
"editor.border": "#FFFFFF",
},
});
editorobj = monaco.editor.create(editorRef.value, {
value: ``,
language: "json",
minimap: {
enabled: false,
},
theme: store.state.theme == "dark" ? "vs-dark" : "myCustomTheme",
automaticLayout: true,
suggestOnTriggerCharacters: false,
wordWrap: "on",
});
editorobj.onKeyUp((e: any) => {
editorData.value = editorobj.getValue();
formData.value.body = editorobj.getValue();
});
editorobj.setValue(formData.value.body);
});
const setupTemplateData = () => {
if (props.template) {
isUpdatingTemplate.value = true;
@@ -291,15 +231,6 @@ const setupTemplateData = () => {
}
};
watch(
() => store.state.theme,
() => {
monaco.editor.setTheme(
store.state.theme == "dark" ? "vs-dark" : "myCustomTheme",
);
},
);
const isTemplateBodyValid = () => {
try {
JSON.parse(formData.value.body);
@@ -406,13 +337,10 @@ const copyTemplateBody = (text: any) => {
};
</script>
<style lang="scss" scoped>
#editor {
.monaco-editor {
width: 100%;
min-height: 310px;
// padding-bottom: 14px;
resize: vertical;
overflow: auto;
max-height: 350px;
min-height: 310px !important;
border-radius: 5px;
}
.example-template-body {
+15 -66
View File
@@ -90,16 +90,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/>
<div class="q-py-md showLabelOnTop text-bold text-h7">Function:</div>
<div
<query-editor
data-test="logs-vrl-function-editor"
ref="editorRef"
id="editor"
:label="t('function.jsfunction')"
stack-label
style="border: 1px solid #dbdbdb; border-radius: 5px"
@keyup="editorUpdate"
class="showLabelOnTop"
resize
></div>
editor-id="add-function-editor"
class="monaco-editor"
v-model:query="formData.function"
language="vrl"
/>
<!-- <q-input v-if="formData.ingest" v-model="formData.order" :label="t('function.order')" color="input-border"
bg-color="input-bg" class="q-py-md showLabelOnTop" stack-label outlined filled dense type="number" min="1" /> -->
@@ -132,14 +130,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<script lang="ts">
import { defineComponent, ref, onMounted, computed, watch } from "vue";
import "monaco-editor/esm/vs/editor/editor.all.js";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
import jsTransformService from "../../services/jstransform";
import { useI18n } from "vue-i18n";
import { useStore } from "vuex";
import { useQuasar } from "quasar";
import segment from "../../services/segment_analytics";
import QueryEditor from "@/components/QueryEditor.vue";
const defaultValue: any = () => {
return {
@@ -164,6 +161,9 @@ export default defineComponent({
default: false,
},
},
components: {
QueryEditor,
},
emits: ["update:list", "cancel:hideform"],
setup(props, { emit }) {
const store: any = useStore();
@@ -188,55 +188,9 @@ export default defineComponent({
const editorUpdate = (e: any) => {
formData.value.function = e.target.value;
};
const editorData = ref("");
const prefixCode = ref("");
const suffixCode = ref("");
onMounted(async () => {
monaco.editor.defineTheme("myCustomTheme", {
base: "vs", // can also be vs-dark or hc-black
inherit: true, // can also be false to completely replace the builtin rules
rules: [
{
token: "comment",
foreground: "ffa500",
fontStyle: "italic underline",
},
{ token: "comment.js", foreground: "008800", fontStyle: "bold" },
{ token: "comment.css", foreground: "0000ff" }, // will inherit fontStyle from `comment` above
],
colors: {
"editor.foreground": "#000000",
},
});
editorobj = monaco.editor.create(editorRef.value, {
value: ``,
language: "vrl",
minimap: {
enabled: false,
},
theme: store.state.theme == "dark" ? "vs-dark" : "myCustomTheme",
});
editorobj.onKeyUp((e: any) => {
if (editorobj.getValue() != "") {
editorData.value = editorobj.getValue();
formData.value.function = editorobj.getValue();
}
});
editorobj.setValue(formData.value.function);
});
watch(
() => store.state.theme,
() => {
monaco.editor.setTheme(
store.state.theme == "dark" ? "vs-dark" : "myCustomTheme"
);
}
);
const isValidParam = () => {
const methodPattern = /^[A-Za-z0-9]+(?:,[A-Za-z0-9]+)*$/g;
return methodPattern.test(formData.value.params) || "Invalid params.";
@@ -256,11 +210,9 @@ end`;
suffixCode.value = ``;
}
const someCode = `${prefixCode.value}
${editorData.value}
formData.value.function = `${prefixCode.value}
${formData.value.function}
${suffixCode.value}`;
editorobj.setValue(someCode);
formData.value.function = editorobj.getValue();
};
const onSubmit = () => {
@@ -270,7 +222,6 @@ end`;
timeout: 2000,
});
formData.value.function = editorobj.getValue();
addJSTransformForm.value.validate().then((valid: any) => {
if (!valid) {
return false;
@@ -349,7 +300,6 @@ end`;
editorobj,
prefixCode,
suffixCode,
editorData,
editorUpdate,
updateEditorContent,
streamTypes,
@@ -377,11 +327,10 @@ end`;
</script>
<style scoped>
#editor {
.monaco-editor {
width: 100%;
min-height: 15rem;
/* padding-bottom: 14px; */
resize: both;
border-radius: 5px;
}
</style>
<style>
+7 -4
View File
@@ -227,10 +227,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
: { width: '100%' }
"
>
<PermissionsJSON
<query-editor
data-test="logs-vrl-function-editor"
editor-id="add-function-editor"
class="monaco-editor q-mt-sm"
language="json"
ref="permissionJsonEditorRef"
v-model:query="permissionsJsonValue"
class="q-mt-sm"
style="height: calc(100vh - 328px)"
/>
</div>
@@ -337,8 +340,8 @@ import AppTabs from "@/components/common/AppTabs.vue";
import GroupUsers from "../groups/GroupUsers.vue";
import { nextTick } from "vue";
const PermissionsJSON = defineAsyncComponent(
() => import("@/components/iam/roles/PermissionsJSON.vue")
const QueryEditor = defineAsyncComponent(
() => import("@/components/QueryEditor.vue")
);
onBeforeMount(() => {
@@ -1,382 +0,0 @@
<!-- Copyright 2023 Zinc Labs Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<div
data-test="query-editor"
class="logs-query-editor"
ref="editorRef"
:id="editorId"
></div>
</template>
<script lang="ts">
import {
defineComponent,
ref,
onMounted,
nextTick,
type Ref,
onDeactivated,
onUnmounted,
onActivated,
watch,
} from "vue";
import "monaco-editor/esm/vs/editor/editor.all.js";
import "monaco-editor/esm/vs/language/json/monaco.contribution.js";
import "monaco-editor/esm/vs/language/json/jsonMode.js";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
import { useStore } from "vuex";
import { debounce } from "quasar";
import useLogs from "@/composables/useLogs";
export default defineComponent({
props: {
editorId: {
type: String,
default: "editor",
},
query: {
type: String,
default: "",
},
showAutoComplete: {
type: Boolean,
default: true,
},
keywords: {
type: Array,
default: () => [],
},
suggestions: {
type: Array,
default: () => [],
},
debounceTime: {
type: Number,
default: 500,
},
readOnly: {
type: Boolean,
default: false,
},
},
emits: ["update-query", "run-query", "update:query"],
setup(props, { emit }) {
const store = useStore();
const editorRef: any = ref();
let editorObj: any = null;
const { searchObj } = useLogs();
let provider: Ref<monaco.IDisposable | null> = ref(null);
const CompletionKind: any = {
Keyword: monaco.languages.CompletionItemKind.Keyword,
Operator: monaco.languages.CompletionItemKind.Operator,
Text: monaco.languages.CompletionItemKind.Text,
Value: monaco.languages.CompletionItemKind.Value,
Method: monaco.languages.CompletionItemKind.Method,
Function: monaco.languages.CompletionItemKind.Function,
Constructor: monaco.languages.CompletionItemKind.Constructor,
Field: monaco.languages.CompletionItemKind.Field,
Variable: monaco.languages.CompletionItemKind.Variable,
};
const insertTextRules: any = {
InsertAsSnippet:
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
KeepWhitespace:
monaco.languages.CompletionItemInsertTextRule.KeepWhitespace,
None: monaco.languages.CompletionItemInsertTextRule.None,
};
const createDependencyProposals = (range: any) => {
return props.keywords.map((keyword: any) => {
const itemObj: any = {
...keyword,
label: keyword["label"],
kind: CompletionKind[keyword["kind"]],
insertText: keyword["insertText"],
range: range,
};
if (insertTextRules[keyword["insertTextRule"]]) {
itemObj["insertTextRules"] =
insertTextRules[keyword["insertTextRule"]];
}
return itemObj;
});
};
onMounted(async () => {
monaco.editor.defineTheme("myCustomTheme", {
base: "vs", // can also be vs-dark or hc-black
inherit: true, // can also be false to completely replace the builtin rules
rules: [{ token: "comment", background: "FFFFFF" }],
colors: {
"editor.foreground": "#000000",
"editor.background": "#fafafa",
"editorCursor.foreground": "#000000",
"editor.lineHighlightBackground": "#FFFFFF",
"editorLineNumber.foreground": "#000000",
"editor.border": "#000000",
},
});
registerAutoCompleteProvider();
const editorElement = document.getElementById(props.editorId);
if (!editorElement) {
console.error("Query Editor element not found");
return;
}
if (editorElement && editorElement?.hasChildNodes()) return;
editorObj = monaco.editor.create(editorElement as HTMLElement, {
value: props.query,
language: "json",
theme: store.state.theme == "dark" ? "vs-dark" : "myCustomTheme",
showFoldingControls: "never",
formatOnPaste: true,
formatOnType: true,
tabSize: 2,
wordWrap: "on",
lineNumbers: "on",
lineNumbersMinChars: 0,
overviewRulerLanes: 0,
fixedOverflowWidgets: false,
overviewRulerBorder: false,
lineDecorationsWidth: 3,
hideCursorInOverviewRuler: true,
renderLineHighlight: "none",
glyphMargin: false,
folding: false,
scrollBeyondLastColumn: 0,
scrollBeyondLastLine: false,
smoothScrolling: true,
mouseWheelScrollSensitivity: 1,
fastScrollSensitivity: 1,
scrollbar: { horizontal: "auto", vertical: "visible" },
find: {
addExtraSpaceOnTop: false,
autoFindInSelection: "never",
seedSearchStringFromSelection: "never",
},
minimap: { enabled: false },
readOnly: props.readOnly,
});
editorObj.onDidChangeModelContent(
debounce((e: any) => {
emit("update-query", e, editorObj.getValue());
emit("update:query", editorObj.getValue());
}, props.debounceTime)
);
editorObj.createContextKey("ctrlenter", true);
editorObj.addCommand(
monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter,
function () {
setTimeout(() => {
emit("run-query");
}, 300);
},
"ctrlenter"
);
editorObj.onDidFocusEditorWidget(() => {
searchObj.meta.queryEditorPlaceholderFlag = false;
});
editorObj.onDidBlurEditorWidget(() => {
searchObj.meta.queryEditorPlaceholderFlag = true;
});
window.addEventListener("click", () => {
editorObj.layout();
});
});
watch(
() => store.state.theme,
() => {
monaco.editor.setTheme(
store.state.theme == "dark" ? "vs-dark" : "myCustomTheme"
);
}
);
onActivated(async () => {
provider.value?.dispose();
registerAutoCompleteProvider();
editorObj.layout();
});
onDeactivated(() => {
provider.value?.dispose();
});
onUnmounted(() => {
provider.value?.dispose();
});
// update readonly when prop value changes
watch(
() => props.readOnly,
() => {
editorObj.updateOptions({ readOnly: props.readOnly });
}
);
// update readonly when prop value changes
watch(
() => props.query,
() => {
if (props.readOnly || !editorObj.hasWidgetFocus()) {
editorObj.getModel().setValue(props.query);
}
}
);
const setValue = (value: string) => {
if (editorObj?.setValue) {
editorObj.setValue(value);
editorObj.layout();
}
};
const registerAutoCompleteProvider = () => {
if (!props.showAutoComplete) return;
provider.value = monaco.languages.registerCompletionItemProvider(
"javascript",
{
provideCompletionItems: function (model, position) {
// find out if we are completing a property in the 'dependencies' object.
var textUntilPosition = model.getValueInRange({
startLineNumber: 1,
startColumn: 1,
endLineNumber: position.lineNumber,
endColumn: position.column,
});
var word = model.getWordUntilPosition(position);
var range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn,
};
let arr = textUntilPosition.trim().split(" ");
let filteredSuggestions = [];
filteredSuggestions = createDependencyProposals(range);
filteredSuggestions = filteredSuggestions.filter((item) => {
return item.label.toLowerCase().includes(word.word.toLowerCase());
});
// if (filteredSuggestions.length == 0) {
const lastElement = arr.pop();
props.suggestions.forEach((suggestion: any) => {
filteredSuggestions.push({
label: suggestion.label(lastElement),
kind: monaco.languages.CompletionItemKind[
suggestion.kind || "Text"
],
insertText: suggestion.insertText(lastElement),
range: range,
});
});
return {
suggestions: filteredSuggestions,
};
},
}
);
};
const resetEditorLayout = () => {
editorObj.layout();
};
const formatDocument = () => {
console.log(
"formatDocument",
editorObj.getAction("editor.action.formatDocument")
);
editorObj.getAction("editor.action.formatDocument").run();
};
const triggerAutoComplete = async (value: string) => {
disableSuggestionPopup();
await nextTick();
editorObj.trigger(value, "editor.action.triggerSuggest", {});
};
const disableSuggestionPopup = () => {
const escEvent = new KeyboardEvent("keydown", {
keyCode: 27,
code: "Escape",
key: "Escape",
bubbles: true,
});
editorRef.value.dispatchEvent(escEvent);
};
const getCursorIndex = () => {
const currentPosition = editorObj.getPosition();
const cursorIndex = editorObj.getModel().getOffsetAt(currentPosition) - 1;
return cursorIndex || null;
};
return {
editorRef,
editorObj,
setValue,
resetEditorLayout,
disableSuggestionPopup,
triggerAutoComplete,
getCursorIndex,
searchObj,
formatDocument,
};
},
});
</script>
<style scoped>
#editor {
width: 100%;
height: 78%;
border-radius: 5px;
}
</style>
<style lang="scss">
.logs-query-editor {
.monaco-editor,
.monaco-editor .monaco-editor {
padding: 0px 0px 0px 0px !important;
.editor-widget .suggest-widget {
z-index: 9999;
display: flex !important;
visibility: visible !important;
}
}
}
</style>
+47 -48
View File
@@ -180,7 +180,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@click.stop="
handleFavoriteSavedView(
props.row,
favoriteViews.includes(props.row.view_id)
favoriteViews.includes(props.row.view_id),
)
"
>
@@ -256,7 +256,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@click.stop="
handleFavoriteSavedView(
props.row,
favoriteViews.includes(props.row.view_id)
favoriteViews.includes(props.row.view_id),
)
"
>
@@ -555,8 +555,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
? 'empty-function'
: ''
"
language="ruby"
@keydown.ctrl.enter="handleRunQueryFn"
language="vrl"
@focus="searchObj.meta.functionEditorPlaceholderFlag = false"
@blur="searchObj.meta.functionEditorPlaceholderFlag = true"
/>
@@ -936,7 +936,7 @@ export default defineComponent({
components: {
DateTime,
QueryEditor: defineAsyncComponent(
() => import("@/components/QueryEditor.vue")
() => import("@/components/QueryEditor.vue"),
),
SyntaxGuide,
AutoRefreshInterval,
@@ -1008,7 +1008,7 @@ export default defineComponent({
query: this.searchObj.data.customDownloadQueryObj,
page_type: this.searchObj.data.stream.streamType,
},
"UI"
"UI",
)
.then((res) => {
this.customDownloadDialog = false;
@@ -1070,7 +1070,6 @@ export default defineComponent({
} = useLogs();
const queryEditorRef = ref(null);
const formData: any = ref(defaultValue());
const functionOptions = ref(searchObj.data.transforms);
@@ -1134,7 +1133,7 @@ export default defineComponent({
(fields) => {
if (fields != undefined && fields.length) updateFieldKeywords(fields);
},
{ immediate: true, deep: true }
{ immediate: true, deep: true },
);
watch(
@@ -1142,7 +1141,7 @@ export default defineComponent({
(funs) => {
if (funs.length) updateFunctionKeywords(funs);
},
{ immediate: true, deep: true }
{ immediate: true, deep: true },
);
onBeforeMount(async () => {
@@ -1265,13 +1264,13 @@ export default defineComponent({
if (searchObj.data.parsedQuery?.from?.length > 0) {
if (
!searchObj.data.stream.selectedStream.includes(
searchObj.data.parsedQuery.from[0].table
searchObj.data.parsedQuery.from[0].table,
) &&
searchObj.data.parsedQuery.from[0].table !== streamName
) {
let streamFound = false;
searchObj.data.stream.selectedStream = [];
streamName = searchObj.data.parsedQuery.from[0].table;
searchObj.data.streamResults.list.forEach((stream) => {
if (stream.name == searchObj.data.parsedQuery.from[0].table) {
@@ -1294,7 +1293,7 @@ export default defineComponent({
// true
// )
// searchObj.data.stream.selectedStreamFields = data.schema
// }
}
});
@@ -1313,7 +1312,7 @@ export default defineComponent({
}
}
} catch (e) {
console.log(e,"Logs: Error while updating query value");
console.log(e, "Logs: Error while updating query value");
}
};
@@ -1341,12 +1340,12 @@ export default defineComponent({
value.selectedDate.from = timestampToTimezoneDate(
value.startTime / 1000,
store.state.timezone,
"yyyy/MM/DD"
"yyyy/MM/DD",
);
value.selectedTime.startTime = timestampToTimezoneDate(
value.startTime / 1000,
store.state.timezone,
"HH:mm"
"HH:mm",
);
dateTimeRef.value.setAbsoluteTime(value.startTime, value.endTime);
@@ -1532,7 +1531,7 @@ export default defineComponent({
if (isSavedFunctionAction.value == "create") {
callTransform = jsTransformService.create(
store.state.selectedOrganization.identifier,
formData.value
formData.value,
);
callTransform
@@ -1574,7 +1573,7 @@ export default defineComponent({
saveFunctionLoader.value = true;
callTransform = jsTransformService.update(
store.state.selectedOrganization.identifier,
formData.value
formData.value,
);
callTransform
@@ -1585,7 +1584,7 @@ export default defineComponent({
});
const transformIndex = searchObj.data.transforms.findIndex(
(obj) => obj.name === formData.value.name
(obj) => obj.name === formData.value.name,
);
if (transformIndex !== -1) {
searchObj.data.transforms[transformIndex].name =
@@ -1694,7 +1693,7 @@ export default defineComponent({
} else {
const needle = val.toLowerCase();
functionOptions.value = searchObj.data.transforms.filter(
(v) => v.name?.toLowerCase().indexOf(needle) > -1
(v) => v.name?.toLowerCase().indexOf(needle) > -1,
);
}
});
@@ -1726,7 +1725,7 @@ export default defineComponent({
savedviewsService
.getViewDetail(
store.state.selectedOrganization.identifier,
item.view_id
item.view_id,
)
.then(async (res) => {
if (res.status == 200) {
@@ -1757,32 +1756,32 @@ export default defineComponent({
// ----- Here we are explicitly handling stream change for multistream -----
let selectedStreams = [];
const streamValues = searchObj.data.stream.streamLists.map(
(item) => item.value
(item) => item.value,
);
if (typeof extractedObj.data.stream.selectedStream == "object") {
if (
extractedObj.data.stream.selectedStream.hasOwnProperty(
"value"
"value",
)
) {
selectedStreams.push(
extractedObj.data.stream.selectedStream.value
extractedObj.data.stream.selectedStream.value,
);
} else {
selectedStreams.push(
...extractedObj.data.stream.selectedStream
...extractedObj.data.stream.selectedStream,
);
}
} else {
selectedStreams.push(extractedObj.data.stream.selectedStream);
}
const streamNotExist = selectedStreams.filter(
(stream_str) => !streamValues.includes(stream_str)
(stream_str) => !streamValues.includes(stream_str),
);
if (streamNotExist.length > 0) {
let errMsg = t("search.streamNotExist").replace(
"[STREAM_NAME]",
streamNotExist
streamNotExist,
);
throw new Error(errMsg);
return;
@@ -1821,7 +1820,7 @@ export default defineComponent({
name: "",
function: searchObj.data.tempFunctionContent,
},
false
false,
);
searchObj.data.tempFunctionContent =
extractedObj.data.tempFunctionContent;
@@ -1832,7 +1831,7 @@ export default defineComponent({
name: "",
function: "",
},
false
false,
);
searchObj.data.tempFunctionContent = "";
searchObj.meta.functionEditorPlaceholderFlag = true;
@@ -1867,15 +1866,15 @@ export default defineComponent({
if (typeof extractedObj.data.stream.selectedStream == "object") {
if (
extractedObj.data.stream.selectedStream.hasOwnProperty(
"value"
"value",
)
) {
selectedStreams.push(
extractedObj.data.stream.selectedStream.value
extractedObj.data.stream.selectedStream.value,
);
} else {
selectedStreams.push(
...extractedObj.data.stream.selectedStream
...extractedObj.data.stream.selectedStream,
);
}
} else {
@@ -1898,7 +1897,7 @@ export default defineComponent({
const streamData = await getStreams(
searchObj.data.stream.streamType,
true
true,
);
searchObj.data.streamResults = streamData;
await loadStreamLists();
@@ -1906,15 +1905,15 @@ export default defineComponent({
// searchObj.value = mergeDeep(searchObj, extractedObj);
const streamValues = searchObj.data.stream.streamLists.map(
(item) => item.value
(item) => item.value,
);
const streamNotExist = selectedStreams.filter(
(stream_str) => !streamValues.includes(stream_str)
(stream_str) => !streamValues.includes(stream_str),
);
if (streamNotExist.length > 0) {
let errMsg = t("search.streamNotExist").replace(
"[STREAM_NAME]",
streamNotExist
streamNotExist,
);
throw new Error(errMsg);
return;
@@ -1926,7 +1925,7 @@ export default defineComponent({
name: "",
function: searchObj.data.tempFunctionContent,
},
false
false,
);
searchObj.data.tempFunctionContent =
extractedObj.data.tempFunctionContent;
@@ -1937,7 +1936,7 @@ export default defineComponent({
name: "",
function: "",
},
false
false,
);
searchObj.data.tempFunctionContent = "";
searchObj.meta.functionEditorPlaceholderFlag = true;
@@ -2029,7 +2028,7 @@ export default defineComponent({
saveViewLoader.value = true;
updateSavedViews(
savedViewSelectedName.value.view_id,
savedViewSelectedName.value.view_name
savedViewSelectedName.value.view_name,
);
});
} else {
@@ -2048,7 +2047,7 @@ export default defineComponent({
savedviewsService
.delete(
store.state.selectedOrganization.identifier,
deleteViewID.value
deleteViewID.value,
)
.then((res) => {
if (res.status == 200) {
@@ -2203,7 +2202,7 @@ export default defineComponent({
searchObj.data.savedViews[index].payload = viewObj.data;
searchObj.data.savedViews[index].view_name = viewName;
}
}
},
);
$q.notify({
@@ -2255,7 +2254,7 @@ export default defineComponent({
const queryString = Object.entries(queryObj)
.map(
([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`,
)
.join("&");
@@ -2285,7 +2284,7 @@ export default defineComponent({
const resetFilters = () => {
if (searchObj.meta.sqlMode == true) {
searchObj.data.query = `SELECT [FIELD_LIST] FROM "${searchObj.data.stream.selectedStream.join(
","
",",
)}" ORDER BY ${store.state.zoConfig.timestamp_column} DESC`;
if (
searchObj.data.stream.interestingFieldList.length > 0 &&
@@ -2293,12 +2292,12 @@ export default defineComponent({
) {
searchObj.data.query = searchObj.data.query.replace(
"[FIELD_LIST]",
searchObj.data.stream.interestingFieldList.join(",")
searchObj.data.stream.interestingFieldList.join(","),
);
} else {
searchObj.data.query = searchObj.data.query.replace(
"[FIELD_LIST]",
"*"
"*",
);
}
} else {
@@ -2343,7 +2342,7 @@ export default defineComponent({
let favoriteViewsList = localSavedViews.value;
if (favoriteViewsList.length > 0) {
favoriteViewsList = favoriteViewsList.filter(
(item) => item.view_id != row.view_id
(item) => item.view_id != row.view_id,
);
// for (const [key, item] of favoriteViewsList.entries()) {
// console.log(item, key);
@@ -2620,7 +2619,7 @@ export default defineComponent({
if (query.toLowerCase().includes("order by")) {
const [beforeOrderBy, afterOrderBy] = queryIndexSplit(
query,
"order by"
"order by",
);
query =
beforeOrderBy.trim() +
@@ -2631,7 +2630,7 @@ export default defineComponent({
} else if (query.toLowerCase().includes("limit")) {
const [beforeLimit, afterLimit] = queryIndexSplit(
query,
"limit"
"limit",
);
query =
beforeLimit.trim() + " AND " + filter + " limit" + afterLimit;
@@ -2642,7 +2641,7 @@ export default defineComponent({
if (query.toLowerCase().includes("order by")) {
const [beforeOrderBy, afterOrderBy] = queryIndexSplit(
query,
"order by"
"order by",
);
query =
beforeOrderBy.trim() +
@@ -2653,7 +2652,7 @@ export default defineComponent({
} else if (query.toLowerCase().includes("limit")) {
const [beforeLimit, afterLimit] = queryIndexSplit(
query,
"limit"
"limit",
);
query =
beforeLimit.trim() +
@@ -0,0 +1,660 @@
// VRL Language Definition
export const vrlLanguageDefinition = {
defaultToken: "invalid",
ignoreCase: true,
tokenPostfix: ".vrl",
brackets: [
{ open: "{", close: "}", token: "delimiter.curly" },
{ open: "[", close: "]", token: "delimiter.square" },
{ open: "(", close: ")", token: "delimiter.parenthesis" },
],
regEx: /\/(?!\/\/)(?:[^\/\\]|\\.)*\/[igm]*/,
keywords: [
"abort",
"as",
"break",
"continue",
"else",
"false",
"for",
"if",
"impl",
"in",
"let",
"loop",
"null",
"return",
"self",
"std",
"then",
"this",
"true",
"type",
"until",
"use",
"while",
],
// we include these common regular expressions
symbols: /[=><!~?&%|+\-*\/\^\.,\:]+/,
escapes:
/\\(?:[abfnrtv\\"'$]|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
// The main tokenizer for our languages
tokenizer: {
root: [
// function invokes fallible
[
/([a-zA-Z_!]+)(\!)(\()/,
{
cases: {
$3: ["keyword", "keyword", ""],
},
// log: 'in function invoke::found::\n\n$0'
},
],
// function invokes
[
/([a-zA-Z_!]+)(\()/,
{
cases: {
$2: ["keyword", ""],
},
// log: 'in function invoke::found::\n\n$0'
},
],
// rstrings
[/r'[^']+'/, { token: "regexp" /*log: 'root_r_string::\n\n$0'*/ }],
// timestamps
[
/t'[^']+'/,
{ token: "regexp" /*log: 'function_arg_r_string::\n\n$0'*/ },
],
// field access, eg: .foo
[
/(\.[^\ \=]+)([\ |\=])/,
{
cases: {
$2: ["entity", ""],
},
},
],
// sstrings
[/s'[^']+'/, { token: "string" /*log: 'root_s_string::\n\n$0'*/ }],
// identifiers and keywords
[/\@[a-zA-Z_]\w*/, "variable.predefined"],
[
/[a-zA-Z_]\w*/,
{
cases: {
this: "variable.predefined",
"@keywords": { token: "keyword.$0" },
"@default": "",
},
},
],
// whitespace
[/[ \t\r\n]+/, ""],
// Comments
[/###/, "comment", "@comment"],
[/#.*$/, "comment"],
// Function invokes
// regular expressions
["///", { token: "regexp", next: "@hereregexp" }],
[/^(\s*)(@regEx)/, ["", "regexp"]],
[/(\()(\s*)(@regEx)/, ["@brackets", "", "regexp"]],
[/(\,)(\s*)(@regEx)/, ["delimiter", "", "regexp"]],
[/(\=)(\s*)(@regEx)/, ["delimiter", "", "regexp"]],
[/(\:)(\s*)(@regEx)/, ["delimiter", "", "regexp"]],
[/(\[)(\s*)(@regEx)/, ["@brackets", "", "regexp"]],
[/(\!)(\s*)(@regEx)/, ["delimiter", "", "regexp"]],
[/(\&)(\s*)(@regEx)/, ["delimiter", "", "regexp"]],
[/(\|)(\s*)(@regEx)/, ["delimiter", "", "regexp"]],
[/(\?)(\s*)(@regEx)/, ["delimiter", "", "regexp"]],
[/(\{)(\s*)(@regEx)/, ["@brackets", "", "regexp"]],
[/(\;)(\s*)(@regEx)/, ["", "", "regexp"]],
// delimiters
[
/}/,
{
cases: {
"$S2==interpolatedstring": {
token: "string",
next: "@pop",
},
"@default": "@brackets",
},
},
],
[/[{}()\[\]]/, "@brackets"],
[/@symbols/, "delimiter"],
// numbers
[/\d+[eE]([\-+]?\d+)?/, "number.float"],
[/\d+\.\d+([eE][\-+]?\d+)?/, "number.float"],
[/0[xX][0-9a-fA-F]+/, "number.hex"],
[/0[0-7]+(?!\d)/, "number.octal"],
[/\d+/, "number"],
// delimiter: after number because of .\d floats
[/[,.]/, "delimiter"],
// strings:
[/"""/, "string", '@herestring."""'],
[/'''/, "string", "@herestring.'''"],
[
/"/,
{
cases: {
"@eos": "string",
"@default": { token: "string", next: '@string."' },
},
},
],
[
/'/,
{
cases: {
"@eos": "string",
"@default": { token: "string", next: "@string.'" },
},
},
],
],
string: [
[/[^"'\#\\]+/, "string"],
[/@escapes/, "string.escape"],
[/\./, "string.escape.invalid"],
[/\./, "string.escape.invalid"],
[
/#{/,
{
cases: {
'$S2=="': {
token: "string",
next: "root.interpolatedstring",
},
"@default": "string",
},
},
],
[
/["']/,
{
cases: {
"$#==$S2": { token: "string", next: "@pop" },
"@default": "string",
},
},
],
[/#/, "string"],
],
herestring: [
[
/("""|''')/,
{
cases: {
"$1==$S2": { token: "string", next: "@pop" },
"@default": "string",
},
},
],
[/[^#\\'"]+/, "string"],
[/['"]+/, "string"],
[/@escapes/, "string.escape"],
[/\./, "string.escape.invalid"],
[/#{/, { token: "string.quote", next: "root.interpolatedstring" }],
[/#/, "string"],
],
comment: [
[/[^#]+/, "comment"],
[/###/, "comment", "@pop"],
[/#/, "comment"],
],
hereregexp: [
[/[^\\\/#]+/, "regexp"],
[/\\./, "regexp"],
[/#.*$/, "comment"],
["///[igm]*", { token: "regexp", next: "@pop" }],
[/\//, "regexp"],
],
function_arg: [
[
/([a-zA-Z_!]+)\(/,
{
cases: {
$S1: {
token: "keyword",
next: "@push",
log: "function_arg_nested::\n\n$0",
},
},
},
],
// we copy the string highlighters from root and apply them to function arguments as well
[/"""/, "string", '@herestring."""'],
[/'''/, "string", "@herestring.'''"],
[
/"/,
{
cases: {
"@eos": "string",
"@default": { token: "string", next: '@string."' },
},
},
],
[
/'/,
{
cases: {
"@eos": "string",
"@default": { token: "string", next: "@string.'" },
},
},
],
// TODO: field access inside function parameters sometimes fails, eg: in a
// newline func(.foo)
// [/(\.)/, { token: "entity" }],
// r strings
[
/r'[^']+'/,
{ token: "regexp" /*log: 'function_arg_r_string::\n\n$0'*/ },
],
[
/t'[^']+'/,
{ token: "regexp" /*log: 'function_arg_r_string::\n\n$0'*/ },
],
// s strings
[/s'[^']+'/, { token: "string" /*log: 'root_s_string::\n\n$0'*/ }],
[
/\)/,
{ token: "", /*log: 'end_off_function_arg::\n\n$0',*/ next: "@pop" },
],
[
/[\.\[\]\,\\\"\%\{\}\$\:\^\w]+/,
{ token: "" /*log: 'function_arg_anything_else::\n\n$0'*/ },
],
],
},
};
export const vrlThemeDefinition = {
base: "vs",
inherit: true,
rules: [
{
background: "ffffff",
token: "",
},
{
foreground: "d73a49",
token: "vrl-function-invokes",
},
{
foreground: "d73a49",
token: "vrl-function-invokes-fallible",
},
{
foreground: "6a737d",
token: "comment",
},
{
foreground: "6a737d",
token: "punctuation.definition.comment",
},
{
foreground: "6a737d",
token: "string.comment",
},
{
foreground: "005cc5",
token: "constant",
},
{
foreground: "005cc5",
token: "entity.name.constant",
},
{
foreground: "005cc5",
token: "variable.other.constant",
},
{
foreground: "005cc5",
token: "variable.language",
},
{
foreground: "6f42c1",
token: "entity",
},
{
foreground: "6f42c1",
token: "entity.name",
},
{
foreground: "24292e",
token: "variable.parameter.function",
},
{
foreground: "22863a",
token: "entity.name.tag",
},
{
foreground: "d73a49",
token: "keyword",
},
{
foreground: "d73a49",
token: "storage",
},
{
foreground: "d73a49",
token: "storage.type",
},
{
foreground: "24292e",
token: "storage.modifier.package",
},
{
foreground: "24292e",
token: "storage.modifier.import",
},
{
foreground: "24292e",
token: "storage.type.java",
},
{
foreground: "032f62",
token: "string",
},
{
foreground: "032f62",
token: "punctuation.definition.string",
},
{
foreground: "032f62",
token: "string punctuation.section.embedded source",
},
{
foreground: "005cc5",
token: "support",
},
{
foreground: "005cc5",
token: "meta.property-name",
},
{
foreground: "e36209",
token: "variable",
},
{
foreground: "24292e",
token: "variable.other",
},
{
foreground: "b31d28",
fontStyle: "bold italic underline",
token: "invalid.broken",
},
{
foreground: "b31d28",
fontStyle: "bold italic underline",
token: "invalid.deprecated",
},
{
foreground: "fafbfc",
background: "b31d28",
fontStyle: "italic underline",
token: "invalid.illegal",
},
{
foreground: "fafbfc",
background: "d73a49",
fontStyle: "italic underline",
token: "carriage-return",
},
{
foreground: "b31d28",
fontStyle: "bold italic underline",
token: "invalid.unimplemented",
},
{
foreground: "b31d28",
token: "message.error",
},
{
foreground: "24292e",
token: "string source",
},
{
foreground: "005cc5",
token: "string variable",
},
{
foreground: "032f62",
token: "source.regexp",
},
{
foreground: "032f62",
token: "string.regexp",
},
{
foreground: "032f62",
token: "string.regexp.character-class",
},
{
foreground: "032f62",
token: "string.regexp constant.character.escape",
},
{
foreground: "032f62",
token: "string.regexp source.ruby.embedded",
},
{
foreground: "032f62",
token: "string.regexp string.regexp.arbitrary-repitition",
},
{
foreground: "22863a",
fontStyle: "bold",
token: "string.regexp constant.character.escape",
},
{
foreground: "005cc5",
token: "support.constant",
},
{
foreground: "005cc5",
token: "support.variable",
},
{
foreground: "005cc5",
token: "meta.module-reference",
},
{
foreground: "735c0f",
token: "markup.list",
},
{
foreground: "005cc5",
fontStyle: "bold",
token: "markup.heading",
},
{
foreground: "005cc5",
fontStyle: "bold",
token: "markup.heading entity.name",
},
{
foreground: "22863a",
token: "markup.quote",
},
{
foreground: "24292e",
fontStyle: "italic",
token: "markup.italic",
},
{
foreground: "24292e",
fontStyle: "bold",
token: "markup.bold",
},
{
foreground: "005cc5",
token: "markup.raw",
},
{
foreground: "b31d28",
background: "ffeef0",
token: "markup.deleted",
},
{
foreground: "b31d28",
background: "ffeef0",
token: "meta.diff.header.from-file",
},
{
foreground: "b31d28",
background: "ffeef0",
token: "punctuation.definition.deleted",
},
{
foreground: "22863a",
background: "f0fff4",
token: "markup.inserted",
},
{
foreground: "22863a",
background: "f0fff4",
token: "meta.diff.header.to-file",
},
{
foreground: "22863a",
background: "f0fff4",
token: "punctuation.definition.inserted",
},
{
foreground: "e36209",
background: "ffebda",
token: "markup.changed",
},
{
foreground: "e36209",
background: "ffebda",
token: "punctuation.definition.changed",
},
{
foreground: "f6f8fa",
background: "005cc5",
token: "markup.ignored",
},
{
foreground: "f6f8fa",
background: "005cc5",
token: "markup.untracked",
},
{
foreground: "6f42c1",
fontStyle: "bold",
token: "meta.diff.range",
},
{
foreground: "005cc5",
token: "meta.diff.header",
},
{
foreground: "005cc5",
fontStyle: "bold",
token: "meta.separator",
},
{
foreground: "005cc5",
token: "meta.output",
},
{
foreground: "586069",
token: "brackethighlighter.tag",
},
{
foreground: "586069",
token: "brackethighlighter.curly",
},
{
foreground: "586069",
token: "brackethighlighter.round",
},
{
foreground: "586069",
token: "brackethighlighter.square",
},
{
foreground: "586069",
token: "brackethighlighter.angle",
},
{
foreground: "586069",
token: "brackethighlighter.quote",
},
{
foreground: "b31d28",
token: "brackethighlighter.unmatched",
},
{
foreground: "b31d28",
token: "sublimelinter.mark.error",
},
{
foreground: "e36209",
token: "sublimelinter.mark.warning",
},
{
foreground: "959da5",
token: "sublimelinter.gutter-mark",
},
{
foreground: "032f62",
fontStyle: "underline",
token: "constant.other.reference.link",
},
{
foreground: "032f62",
fontStyle: "underline",
token: "string.other.link",
},
],
colors: {
"editor.foreground": "#24292e",
"editor.background": "#ffffff",
"editor.selectionBackground": "#c8c8fa",
"editor.inactiveSelectionBackground": "#fafbfc",
"editor.lineHighlightBackground": "#fafbfc",
"editorCursor.foreground": "#24292e",
"editorWhitespace.foreground": "#959da5",
"editorIndentGuide.background": "#959da5",
"editorIndentGuide.activeBackground": "#24292e",
"editor.selectionHighlightBorder": "#fafbfc",
},
};