diff --git a/tests/ui-testing/playwright-tests/alerts.spec.js b/tests/ui-testing/playwright-tests/alerts.spec.js
index 08b3fd7111..65918cd4d2 100644
--- a/tests/ui-testing/playwright-tests/alerts.spec.js
+++ b/tests/ui-testing/playwright-tests/alerts.spec.js
@@ -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();
diff --git a/tests/ui-testing/playwright-tests/sanity.spec.js b/tests/ui-testing/playwright-tests/sanity.spec.js
index 6f3c892f62..f8979cb867 100644
--- a/tests/ui-testing/playwright-tests/sanity.spec.js
+++ b/tests/ui-testing/playwright-tests/sanity.spec.js
@@ -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 });
diff --git a/web/src/components/QueryEditor.vue b/web/src/components/QueryEditor.vue
index a92046358a..9abf25dde9 100644
--- a/web/src/components/QueryEditor.vue
+++ b/web/src/components/QueryEditor.vue
@@ -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,
};
},
});
diff --git a/web/src/components/alerts/AddTemplate.vue b/web/src/components/alerts/AddTemplate.vue
index 0f3a2982e0..2b72020a87 100644
--- a/web/src/components/alerts/AddTemplate.vue
+++ b/web/src/components/alerts/AddTemplate.vue
@@ -81,15 +81,14 @@ along with this program. If not, see .
>
{{ t("alert_templates.body") + " *" }}
-
@@ -169,29 +168,17 @@ along with this program. If not, see .
-
-
diff --git a/web/src/plugins/logs/SearchBar.vue b/web/src/plugins/logs/SearchBar.vue
index 811f1548ec..86fdbcaa2e 100644
--- a/web/src/plugins/logs/SearchBar.vue
+++ b/web/src/plugins/logs/SearchBar.vue
@@ -180,7 +180,7 @@ along with this program. If not, see .
@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 .
@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 .
? '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() +
diff --git a/web/src/utils/query/vrlLanguageDefinition.ts b/web/src/utils/query/vrlLanguageDefinition.ts
new file mode 100644
index 0000000000..d21962dd20
--- /dev/null
+++ b/web/src/utils/query/vrlLanguageDefinition.ts
@@ -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: /[=>