@@ -77,19 +79,19 @@ along with this program. If not, see
.
-
{{
+ {{
vrlFunctionError
}}
@@ -233,6 +235,20 @@ export default defineComponent({
let compilationErr = ref("");
+ // Transform type options for radio buttons
+ const transformTypeOptions = computed(() => {
+ const options = [
+ { label: t("function.vrl"), value: "0" },
+ ];
+
+ // JavaScript functions are only allowed in _meta organization (for SSO claim parsing)
+ if (store.state.selectedOrganization.identifier === "_meta") {
+ options.push({ label: t("function.javascript"), value: "1" });
+ }
+
+ return options;
+ });
+
const beingUpdated = computed(() => props.isUpdated);
const streamTypes = ["logs", "metrics", "traces"];
@@ -315,14 +331,10 @@ export default defineComponent({
);
};
const updateEditorContent = () => {
- if (formData.value.transType == "1") {
- prefixCode.value = `function(row)`;
- suffixCode.value = `
-end`;
- } else {
- prefixCode.value = ``;
- suffixCode.value = ``;
- }
+ // JS functions don't need prefix/suffix, only VRL functions might
+ // For now, both VRL and JS are written as-is
+ prefixCode.value = ``;
+ suffixCode.value = ``;
formData.value.function = `${prefixCode.value}
${formData.value.function}
@@ -352,10 +364,8 @@ end`;
try {
if (!beingUpdated.value) {
formData.value.transType = parseInt(formData.value.transType);
- //trans type is lua remove params from form
- if (formData.value.transType == 1) {
- formData.value.params = "";
- }
+ // Both VRL and JS use params field (e.g., "row")
+ // No need to clear params for JS
callTransform = jsTransformService.create(
store.state.selectedOrganization.identifier,
@@ -363,10 +373,8 @@ end`;
);
} else {
formData.value.transType = parseInt(formData.value.transType);
- //trans type is lua remove params from form
- if (formData.value.transType == 1) {
- formData.value.params = "";
- }
+ // Both VRL and JS use params field (e.g., "row")
+ // No need to clear params for JS
callTransform = jsTransformService.update(
store.state.selectedOrganization.identifier,
@@ -425,6 +433,10 @@ end`;
const handleFunctionError = (err: string) => {
vrlFunctionError.value = err;
+ // Auto-expand error section when error occurs
+ if (err) {
+ expandState.value.functionError = true;
+ }
};
const closeAddFunction = () => {
@@ -514,6 +526,7 @@ end`;
splitterModel,
closeAddFunction,
confirmDialogMeta,
+ transformTypeOptions,
resetConfirmDialog,
cancelAddFunction,
openChat,
@@ -534,6 +547,10 @@ end`;
this.beingUpdated = true;
this.disableColor = "grey-5";
this.formData = this.modelValue;
+ // Ensure transType is a string for radio button binding
+ if (this.formData.transType !== undefined) {
+ this.formData.transType = String(this.formData.transType);
+ }
}
},
});
diff --git a/web/src/components/functions/FunctionList.vue b/web/src/components/functions/FunctionList.vue
index f9a99adb14..f8a7a92f88 100644
--- a/web/src/components/functions/FunctionList.vue
+++ b/web/src/components/functions/FunctionList.vue
@@ -43,6 +43,7 @@ along with this program. If not, see
.
flat
no-caps
:label="t(`function.add`)"
+ data-test="function-list-add-function-btn"
@click="showAddUpdateFn({})"
/>
diff --git a/web/src/components/functions/FunctionsToolbar.vue b/web/src/components/functions/FunctionsToolbar.vue
index 3b592383db..e6f577efa8 100644
--- a/web/src/components/functions/FunctionsToolbar.vue
+++ b/web/src/components/functions/FunctionsToolbar.vue
@@ -20,7 +20,7 @@
+
+
+
+
+
+ {{ transformTypeOptions[0]?.label }}
+
+
+
+
+ {{ transformTypeOptions[1]?.label }}
+
+
+
+
+ {{ selectedTransType === '1' ? t('function.javascript') : t('function.vrl') }} Tip:
+ {{ selectedTransType === '1' ? t('function.jsFunctionHint') : t('function.vrlFunctionHint') }}
+
+
+
@@ -130,9 +163,9 @@ import { useI18n } from "vue-i18n";
import { useQuasar } from "quasar";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
-import { outlinedInfo } from "@quasar/extras/material-icons-outlined";
import config from "../../aws-exports";
import { getImageURL } from "@/utils/zincutils";
+import { outlinedInfo } from "@quasar/extras/material-icons-outlined";
const { t } = useI18n();
const q = useQuasar();
@@ -154,9 +187,17 @@ const props = defineProps({
type: Boolean,
default: true,
},
+ transType: {
+ type: String,
+ default: "0",
+ },
+ transformTypeOptions: {
+ type: Array,
+ default: () => [],
+ },
});
-const emit = defineEmits(["test", "save", "update:name", "back", "cancel", "open:chat"]);
+const emit = defineEmits(["test", "save", "update:name", "back", "cancel", "open:chat", "update:transType"]);
const addFunctionForm = ref(null);
@@ -181,6 +222,14 @@ const functionName = computed({
set: (value) => emit("update:name", value),
});
+const selectedTransType = computed({
+ get: () => {
+ // Ensure the value is always a string for radio button comparison
+ return String(props.transType || "0");
+ },
+ set: (value) => emit("update:transType", value),
+});
+
const isAddFunctionComponent = computed(() => router.currentRoute.value.path.includes('functions'))
const handleFullScreen = () => {
q.fullscreen.toggle();
diff --git a/web/src/components/functions/TestFunction.vue b/web/src/components/functions/TestFunction.vue
index cd6a77f104..9ae588f368 100644
--- a/web/src/components/functions/TestFunction.vue
+++ b/web/src/components/functions/TestFunction.vue
@@ -628,16 +628,25 @@ const processTestResults = async (results: any) => {
};
const handleTestError = (err: any) => {
- const errMsg = err.response?.data?.message || "Error in testing function";
- outputEventsErrorMsg.value = "Error while transforming results";
+ const rawErrMsg = err.response?.data?.message || "Error in testing function";
+ const isJSFunction = String(props.vrlFunction.transType) === '1';
+
+ // Display the raw error message from the backend without modification
+ // The backend now extracts detailed error information from rquickjs
+ outputEventsErrorMsg.value = isJSFunction
+ ? "JavaScript error - see details below"
+ : "Error while transforming results";
q.notify({
type: "negative",
- message: errMsg,
- timeout: 3000,
+ message: rawErrMsg,
+ timeout: 5000,
});
- outputEvents.value = errMsg;
+ outputEvents.value = rawErrMsg;
+
+ // Emit error to parent for display in error section
+ emit("function-error", rawErrMsg);
};
const testFunction = async () => {
@@ -651,6 +660,7 @@ const testFunction = async () => {
const payload = {
function: props.vrlFunction.function,
events: JSON.parse(inputEvents.value),
+ trans_type: props.vrlFunction.transType ? parseInt(props.vrlFunction.transType) : undefined,
};
jstransform
.test(store.state.selectedOrganization.identifier, payload)
diff --git a/web/src/components/pipeline/PipelineEditor.vue b/web/src/components/pipeline/PipelineEditor.vue
index c448d0d472..c132c8608a 100644
--- a/web/src/components/pipeline/PipelineEditor.vue
+++ b/web/src/components/pipeline/PipelineEditor.vue
@@ -671,8 +671,12 @@ const getFunctions = () => {
functions.value = {};
functionOptions.value = [];
res.data.list.forEach((func: Function) => {
- functions.value[func.name] = func;
- functionOptions.value.push(func.name);
+ // Only include VRL functions (trans_type === 0 or undefined)
+ // JavaScript functions (trans_type === 1) cannot be used in pipelines
+ if (func.trans_type !== 1) {
+ functions.value[func.name] = func;
+ functionOptions.value.push(func.name);
+ }
});
})
.finally(() => {
@@ -1066,8 +1070,12 @@ const onNodeDragOver = (event: any) => {
const updateNewFunction = (_function: Function) => {
if (!functions.value[_function.name]) {
- functions.value[_function.name] = _function;
- functionOptions.value.push(_function.name);
+ // Only add VRL functions (trans_type !== 1) to pipeline options
+ // JavaScript functions cannot be used in pipelines
+ if (_function.trans_type !== 1) {
+ functions.value[_function.name] = _function;
+ functionOptions.value.push(_function.name);
+ }
}
};
diff --git a/web/src/components/settings/DomainManagement.vue b/web/src/components/settings/DomainManagement.vue
index 1e2853ac26..557f1ce3ef 100644
--- a/web/src/components/settings/DomainManagement.vue
+++ b/web/src/components/settings/DomainManagement.vue
@@ -65,6 +65,7 @@ along with this program. If not, see .
unelevated
@click="saveClaimParserFunction"
:loading="savingClaimParser"
+ :disable="!hasClaimParserChanged"
/>
@@ -357,7 +358,7 @@ along with this program. If not, see .