mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-06 00:09:22 +00:00
fix: Actions UI issues (#6325)
1. Action Issues 2. #6288 --------- Co-authored-by: udev <uddhavdave1@gmail.com>
This commit is contained in:
@@ -79,6 +79,8 @@ pub struct Endpoint {
|
||||
pub skip_tls_verify: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub headers: Option<HashMap<String, String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub action_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
||||
@@ -47,8 +47,17 @@ impl From<meta_dest::Destination> for Destination {
|
||||
method: endpoint.method,
|
||||
skip_tls_verify: endpoint.skip_tls_verify,
|
||||
headers: endpoint.headers,
|
||||
#[cfg(feature = "enterprise")]
|
||||
destination_type: if endpoint.action_id.is_some() {
|
||||
DestinationType::Action
|
||||
} else {
|
||||
DestinationType::Http
|
||||
},
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
destination_type: DestinationType::Http,
|
||||
template: Some(template),
|
||||
#[cfg(feature = "enterprise")]
|
||||
action_id: endpoint.action_id,
|
||||
..Default::default()
|
||||
},
|
||||
meta_dest::DestinationType::Sns(aws_sns) => Self {
|
||||
@@ -87,6 +96,7 @@ impl Destination {
|
||||
method: self.method,
|
||||
skip_tls_verify: self.skip_tls_verify,
|
||||
headers: self.headers,
|
||||
action_id: None,
|
||||
})
|
||||
}
|
||||
DestinationType::Sns => meta_dest::DestinationType::Sns(meta_dest::AwsSns {
|
||||
@@ -95,18 +105,25 @@ impl Destination {
|
||||
}),
|
||||
#[cfg(feature = "enterprise")]
|
||||
DestinationType::Action => {
|
||||
let action_endpoint = ActionEndpoint::new(&org_id, &self.action_id)
|
||||
.map_err(DestinationError::InvalidActionId)?;
|
||||
meta_dest::DestinationType::Http(meta_dest::Endpoint {
|
||||
url: action_endpoint.url,
|
||||
method: if action_endpoint.method == reqwest::Method::POST {
|
||||
meta_dest::HTTPType::POST
|
||||
} else {
|
||||
meta_dest::HTTPType::GET
|
||||
},
|
||||
skip_tls_verify: action_endpoint.skip_tls,
|
||||
headers: None,
|
||||
})
|
||||
if let Some(action_id) = self.action_id {
|
||||
let action_endpoint = ActionEndpoint::new(&org_id, &action_id)
|
||||
.map_err(DestinationError::InvalidActionId)?;
|
||||
meta_dest::DestinationType::Http(meta_dest::Endpoint {
|
||||
url: action_endpoint.url,
|
||||
method: if action_endpoint.method == reqwest::Method::POST {
|
||||
meta_dest::HTTPType::POST
|
||||
} else {
|
||||
meta_dest::HTTPType::GET
|
||||
},
|
||||
skip_tls_verify: action_endpoint.skip_tls,
|
||||
headers: None,
|
||||
action_id: Some(action_id),
|
||||
})
|
||||
} else {
|
||||
return Err(DestinationError::InvalidActionId(anyhow::anyhow!(
|
||||
"Action id is required for action destination"
|
||||
)));
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(meta_dest::Destination {
|
||||
@@ -125,6 +142,7 @@ impl Destination {
|
||||
method: self.method,
|
||||
skip_tls_verify: self.skip_tls_verify,
|
||||
headers: self.headers,
|
||||
action_id: None,
|
||||
};
|
||||
Ok(meta_dest::Destination {
|
||||
id: None,
|
||||
@@ -204,8 +222,8 @@ pub struct Destination {
|
||||
pub destination_type: DestinationType,
|
||||
/// Required when `destination_type` is `Action`
|
||||
#[cfg(feature = "enterprise")]
|
||||
#[serde(default)]
|
||||
pub action_id: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub action_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Default, PartialEq, Eq, Deserialize, Clone, ToSchema)]
|
||||
|
||||
@@ -160,7 +160,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
:rules="[
|
||||
(val: any) => {
|
||||
if (!isEditingActionScript) {
|
||||
return !!val || 'CSV File is required!';
|
||||
return !!val || 'ZIP File is required!';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@@ -847,7 +847,10 @@ const saveActionScript = async () => {
|
||||
// );
|
||||
|
||||
// Add cron expression if needed
|
||||
if (frequency.value.type === "repeat") {
|
||||
if (
|
||||
formData.value.type === "scheduled" &&
|
||||
frequency.value.type === "repeat"
|
||||
) {
|
||||
commonFields.cron_expr = frequency.value.cron.toString().trim();
|
||||
// commonFields.timezoneOffset = convertedDateTime.offset.toString();
|
||||
// commonFields.timezone = scheduling.value.timezone;
|
||||
|
||||
@@ -165,11 +165,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</div>
|
||||
<div v-else>
|
||||
<ImportDestination
|
||||
:destinations="destinations"
|
||||
:templates="templates"
|
||||
@update:destinations="getDestinations"
|
||||
/>
|
||||
|
||||
:destinations="destinations"
|
||||
:templates="templates"
|
||||
@update:destinations="getDestinations"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
@@ -182,7 +181,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</q-page>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { ref, onBeforeMount, onActivated, watch, defineComponent, onMounted } from "vue";
|
||||
import {
|
||||
ref,
|
||||
onBeforeMount,
|
||||
onActivated,
|
||||
watch,
|
||||
defineComponent,
|
||||
onMounted,
|
||||
} from "vue";
|
||||
import type { Ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useQuasar, type QTableProps } from "quasar";
|
||||
@@ -200,6 +206,7 @@ import type { Template } from "@/ts/interfaces/index";
|
||||
|
||||
import { outlinedDelete } from "@quasar/extras/material-icons-outlined";
|
||||
import ImportDestination from "./ImportDestination.vue";
|
||||
import useActions from "@/composables/useActions";
|
||||
|
||||
interface ConformDelete {
|
||||
visible: boolean;
|
||||
@@ -207,13 +214,20 @@ interface ConformDelete {
|
||||
}
|
||||
export default defineComponent({
|
||||
name: "PageAlerts",
|
||||
components: { AddDestination, NoData, ConfirmDialog, QTablePagination ,ImportDestination},
|
||||
components: {
|
||||
AddDestination,
|
||||
NoData,
|
||||
ConfirmDialog,
|
||||
QTablePagination,
|
||||
ImportDestination,
|
||||
},
|
||||
setup() {
|
||||
const qTable = ref();
|
||||
const store = useStore();
|
||||
const editingDestination: Ref<DestinationPayload | null> = ref(null);
|
||||
const { t } = useI18n();
|
||||
const q = useQuasar();
|
||||
const { getAllActions } = useActions();
|
||||
const columns: any = ref<QTableProps["columns"]>([
|
||||
{
|
||||
name: "#",
|
||||
@@ -283,21 +297,39 @@ export default defineComponent({
|
||||
onBeforeMount(() => {
|
||||
getDestinations();
|
||||
getTemplates();
|
||||
getActions();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => router.currentRoute.value.query.action,
|
||||
(action) => {
|
||||
if (!action) {
|
||||
showDestinationEditor.value = false
|
||||
showImportDestination.value = false
|
||||
};
|
||||
}
|
||||
showDestinationEditor.value = false;
|
||||
showImportDestination.value = false;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(()=>{
|
||||
onMounted(() => {
|
||||
updateRoute();
|
||||
})
|
||||
});
|
||||
|
||||
const getActions = async () => {
|
||||
const dismiss = q.notify({
|
||||
spinner: true,
|
||||
message: "Please wait while loading actions...",
|
||||
});
|
||||
if (store.state.organizationData.actions.length == 0) {
|
||||
await getAllActions()
|
||||
.catch(() => {
|
||||
q.notify({
|
||||
type: "negative",
|
||||
message: "Error while loading actions.",
|
||||
});
|
||||
})
|
||||
.finally(() => dismiss());
|
||||
}
|
||||
};
|
||||
|
||||
const getDestinations = () => {
|
||||
const dismiss = q.notify({
|
||||
@@ -316,7 +348,9 @@ export default defineComponent({
|
||||
.then((res) => {
|
||||
res.data = res.data.filter(
|
||||
(destination: any) =>
|
||||
destination.type == "http" || destination.type == "email",
|
||||
destination.type == "http" ||
|
||||
destination.type == "email" ||
|
||||
destination.type === "action",
|
||||
);
|
||||
resultTotal.value = res.data.length;
|
||||
destinations.value = res.data.map((data: any, index: number) => ({
|
||||
@@ -326,7 +360,7 @@ export default defineComponent({
|
||||
updateRoute();
|
||||
})
|
||||
.catch((err) => {
|
||||
if(err.response.status != 403){
|
||||
if (err.response.status != 403) {
|
||||
q.notify({
|
||||
type: "negative",
|
||||
message: "Error while pulling destinations.",
|
||||
@@ -349,14 +383,14 @@ export default defineComponent({
|
||||
editDestination(null);
|
||||
if (router.currentRoute.value.query.action === "update")
|
||||
editDestination(
|
||||
getDestinationByName(router.currentRoute.value.query.name as string)
|
||||
getDestinationByName(router.currentRoute.value.query.name as string),
|
||||
);
|
||||
if(router.currentRoute.value.query.action === "import")
|
||||
if (router.currentRoute.value.query.action === "import")
|
||||
showImportDestination.value = true;
|
||||
};
|
||||
const getDestinationByName = (name: string) => {
|
||||
return destinations.value.find(
|
||||
(destination) => destination.name === name
|
||||
(destination) => destination.name === name,
|
||||
);
|
||||
};
|
||||
const editDestination = (destination: any) => {
|
||||
@@ -459,28 +493,27 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
|
||||
const exportDestination = (row: any) =>{
|
||||
|
||||
const exportDestination = (row: any) => {
|
||||
const findDestination: any = getDestinationByName(row.name);
|
||||
const destinationByName = {...findDestination}
|
||||
if(destinationByName.hasOwnProperty("#")) delete destinationByName["#"];
|
||||
const destinationJson = JSON.stringify(destinationByName,null,2);
|
||||
const blob = new Blob([destinationJson], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
// Create an anchor element to trigger the download
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
const destinationByName = { ...findDestination };
|
||||
if (destinationByName.hasOwnProperty("#")) delete destinationByName["#"];
|
||||
const destinationJson = JSON.stringify(destinationByName, null, 2);
|
||||
const blob = new Blob([destinationJson], { type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
// Create an anchor element to trigger the download
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
|
||||
// Set the filename of the download
|
||||
link.download = `${destinationByName.name}.json`;
|
||||
// Set the filename of the download
|
||||
link.download = `${destinationByName.name}.json`;
|
||||
|
||||
// Trigger the download by simulating a click
|
||||
link.click();
|
||||
// Trigger the download by simulating a click
|
||||
link.click();
|
||||
|
||||
// Clean up the URL object after download
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
const importDestination = () =>{
|
||||
// Clean up the URL object after download
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
const importDestination = () => {
|
||||
showImportDestination.value = true;
|
||||
router.push({
|
||||
name: "alertDestinations",
|
||||
@@ -489,7 +522,7 @@ export default defineComponent({
|
||||
org_identifier: store.state.selectedOrganization.identifier,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
t,
|
||||
|
||||
@@ -222,7 +222,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
dense
|
||||
tabindex="0"
|
||||
@update:model-value="
|
||||
updateDestinationName(userSelectedDestinationName[index], index)
|
||||
updateDestinationName(
|
||||
userSelectedDestinationName[index],
|
||||
index,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
@@ -250,7 +253,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
dense
|
||||
tabindex="0"
|
||||
@update:model-value="
|
||||
updateDestinationUrl(userSelectedDestinationUrl[index], index)
|
||||
updateDestinationUrl(
|
||||
userSelectedDestinationUrl[index],
|
||||
index,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
@@ -284,7 +290,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
fill-input
|
||||
:input-debounce="400"
|
||||
@update:model-value="
|
||||
updateDestinationType(userSelectedDestinationType[index], index)
|
||||
updateDestinationType(
|
||||
userSelectedDestinationType[index],
|
||||
index,
|
||||
)
|
||||
"
|
||||
behavior="menu"
|
||||
/>
|
||||
@@ -318,7 +327,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
fill-input
|
||||
:input-debounce="400"
|
||||
@update:model-value="
|
||||
updateDestinationMethod(userSelectedDestinationMethod[index], index)
|
||||
updateDestinationMethod(
|
||||
userSelectedDestinationMethod[index],
|
||||
index,
|
||||
)
|
||||
"
|
||||
behavior="menu"
|
||||
/>
|
||||
@@ -353,11 +365,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
hide-selected
|
||||
fill-input
|
||||
@filter="filterTemplates"
|
||||
:rules="[
|
||||
(val) => !!val || 'Field is required!',
|
||||
]"
|
||||
:rules="[(val) => !!val || 'Field is required!']"
|
||||
style="width: 300px"
|
||||
@update:model-value="updateDestinationTemplate($event, index)"
|
||||
@update:model-value="
|
||||
updateDestinationTemplate($event, index)
|
||||
"
|
||||
>
|
||||
</q-select>
|
||||
</div>
|
||||
@@ -384,11 +396,54 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
dense
|
||||
tabindex="0"
|
||||
@update:model-value="
|
||||
updateDestinationEmails(userSelectedEmails[index], index)
|
||||
updateDestinationEmails(
|
||||
userSelectedEmails[index],
|
||||
index,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
<span
|
||||
class="text-red"
|
||||
v-else-if="
|
||||
typeof errorMessage === 'object' &&
|
||||
errorMessage.field == 'action_id'
|
||||
"
|
||||
>
|
||||
{{ errorMessage.message }}
|
||||
<div>
|
||||
<q-select
|
||||
data-test="destination-import-template-input"
|
||||
v-model="userSelectedActionId[index]"
|
||||
:options="filteredActions"
|
||||
label="Templates *"
|
||||
:popup-content-style="{
|
||||
textTransform: 'lowercase',
|
||||
}"
|
||||
color="input-border"
|
||||
bg-color="input-bg"
|
||||
class="q-py-sm showLabelOnTop no-case"
|
||||
filled
|
||||
stack-label
|
||||
dense
|
||||
use-input
|
||||
emit-value
|
||||
map-options
|
||||
input-debounce="400"
|
||||
behavior="menu"
|
||||
hide-selected
|
||||
fill-input
|
||||
@filter="filterActions"
|
||||
:rules="[(val) => !!val || 'Field is required!']"
|
||||
style="width: 300px"
|
||||
@update:model-value="
|
||||
updateDestinationAction($event, index)
|
||||
"
|
||||
>
|
||||
</q-select>
|
||||
</div>
|
||||
</span>
|
||||
<span
|
||||
class="text-red"
|
||||
v-else-if="
|
||||
@@ -400,7 +455,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<div style="width: 300px">
|
||||
<q-toggle
|
||||
data-test="destination-import-skip-tls-verify-input"
|
||||
:model-value="userSelectedSkipTlsVerify[index] ?? false"
|
||||
:model-value="
|
||||
userSelectedSkipTlsVerify[index] ?? false
|
||||
"
|
||||
:label="t('alert_destinations.skip_tls_verify')"
|
||||
class="q-mt-sm"
|
||||
@update:model-value="
|
||||
@@ -416,7 +473,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</div>
|
||||
|
||||
<div class="error-section" v-if="destinationCreators.length > 0">
|
||||
<div class="section-title text-primary" data-test="destination-import-creation-title">Destination Creation</div>
|
||||
<div
|
||||
class="section-title text-primary"
|
||||
data-test="destination-import-creation-title"
|
||||
>
|
||||
Destination Creation
|
||||
</div>
|
||||
<div
|
||||
class="error-list"
|
||||
v-for="(val, index) in destinationCreators"
|
||||
@@ -518,6 +580,7 @@ export default defineComponent({
|
||||
const activeTab = ref("import_json_file");
|
||||
const splitterModel = ref(60);
|
||||
const filteredTemplates = ref<string[]>([]);
|
||||
const filteredActions = ref<string[]>([]);
|
||||
|
||||
const userSelectedSkipTlsVerify = ref<boolean[]>([]);
|
||||
|
||||
@@ -525,8 +588,10 @@ export default defineComponent({
|
||||
return props.templates
|
||||
.filter((template: any) => {
|
||||
// Get the current destination type
|
||||
const currentDestinationType = jsonArrayOfObj.value[destinationErrorsToDisplay.value.length - 1]?.type;
|
||||
|
||||
const currentDestinationType =
|
||||
jsonArrayOfObj.value[destinationErrorsToDisplay.value.length - 1]
|
||||
?.type;
|
||||
|
||||
if (currentDestinationType === "email" && template.type === "email") {
|
||||
return true;
|
||||
} else if (currentDestinationType !== "email") {
|
||||
@@ -539,6 +604,8 @@ export default defineComponent({
|
||||
|
||||
const userSelectedEmails = ref([]);
|
||||
|
||||
const userSelectedActionId = ref([]);
|
||||
|
||||
const updateDestinationType = (type: any, index: number) => {
|
||||
jsonArrayOfObj.value[index].type = type;
|
||||
jsonStr.value = JSON.stringify(jsonArrayOfObj.value, null, 2);
|
||||
@@ -562,9 +629,14 @@ export default defineComponent({
|
||||
jsonStr.value = JSON.stringify(jsonArrayOfObj.value, null, 2);
|
||||
};
|
||||
|
||||
const updateDestinationAction = (id: string, index: number) => {
|
||||
jsonArrayOfObj.value[index].action_id = id;
|
||||
jsonStr.value = JSON.stringify(jsonArrayOfObj.value, null, 2);
|
||||
};
|
||||
|
||||
const updateDestinationEmails = (emails: string, index: number) => {
|
||||
// Split comma-separated emails and trim whitespace
|
||||
const emailArray = emails.split(',').map(email => email.trim());
|
||||
const emailArray = emails.split(",").map((email) => email.trim());
|
||||
jsonArrayOfObj.value[index].emails = emailArray;
|
||||
jsonStr.value = JSON.stringify(jsonArrayOfObj.value, null, 2);
|
||||
};
|
||||
@@ -586,15 +658,53 @@ export default defineComponent({
|
||||
update(() => {
|
||||
const needle = val.toLowerCase();
|
||||
filteredTemplates.value = getFormattedTemplates.value.filter(
|
||||
(template: string) => template.toLowerCase().includes(needle)
|
||||
(template: string) => template.toLowerCase().includes(needle),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const filterActions = (val: string, update: Function) => {
|
||||
if (val === "") {
|
||||
update(() => {
|
||||
filteredActions.value = store.state.organizationData.actions
|
||||
.filter(
|
||||
(action: any) => action.execution_details_type === "service",
|
||||
)
|
||||
.map((action: any) => ({
|
||||
label: action.name,
|
||||
value: action.id,
|
||||
}));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
update(() => {
|
||||
const needle = val.toLowerCase();
|
||||
getServiceActions()
|
||||
.map((action: any) => ({
|
||||
label: action.name,
|
||||
value: action.id,
|
||||
}))
|
||||
.filter(
|
||||
(action: { name: string; id: string }) =>
|
||||
action.name.toLowerCase().includes(needle) ||
|
||||
action.id.toLowerCase().includes(needle),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const getServiceActions = () => {
|
||||
return (
|
||||
store.state.organizationData.actions.filter(
|
||||
(action: any) => action.execution_details_type === "service",
|
||||
) || []
|
||||
);
|
||||
};
|
||||
|
||||
watch(jsonFiles, async (newVal: any, oldVal: any) => {
|
||||
if (newVal && newVal.length > 0) {
|
||||
let combinedJson: any[] = [];
|
||||
|
||||
|
||||
for (const file of newVal) {
|
||||
try {
|
||||
const result: any = await new Promise((resolve) => {
|
||||
@@ -603,7 +713,9 @@ export default defineComponent({
|
||||
try {
|
||||
const parsedJson = JSON.parse(e.target.result);
|
||||
// Convert to array if it's a single object
|
||||
const jsonArray = Array.isArray(parsedJson) ? parsedJson : [parsedJson];
|
||||
const jsonArray = Array.isArray(parsedJson)
|
||||
? parsedJson
|
||||
: [parsedJson];
|
||||
resolve(jsonArray);
|
||||
} catch (error) {
|
||||
q.notify({
|
||||
@@ -617,13 +729,13 @@ export default defineComponent({
|
||||
};
|
||||
reader.readAsText(file);
|
||||
});
|
||||
|
||||
|
||||
combinedJson = [...combinedJson, ...result];
|
||||
} catch (error) {
|
||||
console.error('Error reading file:', error);
|
||||
console.error("Error reading file:", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update the refs with combined JSON data
|
||||
jsonArrayOfObj.value = combinedJson;
|
||||
jsonStr.value = JSON.stringify(combinedJson, null, 2);
|
||||
@@ -697,7 +809,9 @@ export default defineComponent({
|
||||
throw new Error("JSON string is empty");
|
||||
} else {
|
||||
const parsedJson = JSON.parse(jsonStr.value);
|
||||
jsonArrayOfObj.value = Array.isArray(parsedJson) ? parsedJson : [parsedJson];
|
||||
jsonArrayOfObj.value = Array.isArray(parsedJson)
|
||||
? parsedJson
|
||||
: [parsedJson];
|
||||
}
|
||||
} catch (e: any) {
|
||||
q.notify({
|
||||
@@ -801,10 +915,11 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
if (
|
||||
input.type == "http" && (!input.method ||
|
||||
(input.method !== "post" &&
|
||||
input.method !== "get" &&
|
||||
input.method !== "put"))
|
||||
input.type == "http" &&
|
||||
(!input.method ||
|
||||
(input.method !== "post" &&
|
||||
input.method !== "get" &&
|
||||
input.method !== "put"))
|
||||
) {
|
||||
destinationErrors.push({
|
||||
message: `Destination - ${index} 'method' is required and should be either 'post', 'get', or 'put'`,
|
||||
@@ -828,22 +943,39 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
|
||||
// Check type for email and it should be present only for email
|
||||
if (input.type != "email" && input.type != "http") {
|
||||
const availableActions = getServiceActions().map(
|
||||
(action: any) => action.id,
|
||||
);
|
||||
|
||||
if (
|
||||
input.type === "action" &&
|
||||
!availableActions.includes(input.action_id)
|
||||
) {
|
||||
destinationErrors.push({
|
||||
message: `Destination - ${index} 'type' should be email or http`,
|
||||
field: "type",
|
||||
message: `Destination - ${index} action "${input.action_id}" does not exist for type "${input.type}"`,
|
||||
field: "action_id",
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Check type for email and it should be present only for email
|
||||
if (
|
||||
input.type != "email" &&
|
||||
input.type != "http" &&
|
||||
input.type !== "action"
|
||||
) {
|
||||
destinationErrors.push({
|
||||
message: `Destination - ${index} 'type' should be email, http or action`,
|
||||
field: "type",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if 'method' is required for both webhook and email
|
||||
|
||||
// Check if 'skip_tls_verify' is required for both webhook and email, and it should be a boolean
|
||||
if (
|
||||
input.type == "http" && (input.skip_tls_verify === undefined ||
|
||||
typeof input.skip_tls_verify !== "boolean")
|
||||
input.type == "http" &&
|
||||
(input.skip_tls_verify === undefined ||
|
||||
typeof input.skip_tls_verify !== "boolean")
|
||||
) {
|
||||
destinationErrors.push({
|
||||
message: `Destination - ${index} 'skip_tls_verify' is required and should be a boolean value`,
|
||||
@@ -851,27 +983,24 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if(input.type == 'http' && input.headers != undefined) {
|
||||
if(typeof input.headers !== 'object') {
|
||||
if (input.type == "http" && input.headers != undefined) {
|
||||
if (typeof input.headers !== "object") {
|
||||
destinationErrors.push(
|
||||
`Destination - ${index} 'headers' should be an object for webhook`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (input.type === "email" && input.hasOwnProperty('headers') && Object.keys(input.headers).length !== 0) {
|
||||
if (
|
||||
input.type === "email" &&
|
||||
input.hasOwnProperty("headers") &&
|
||||
Object.keys(input.headers).length !== 0
|
||||
) {
|
||||
destinationErrors.push(
|
||||
`Destination - ${index} 'headers' should not be provided for email`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Check if 'name' is required for both webhook and email
|
||||
if (!input.name || typeof input.name !== "string") {
|
||||
destinationErrors.push(
|
||||
@@ -879,7 +1008,6 @@ export default defineComponent({
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 'emails' should be required for email type and should be an array of strings
|
||||
if (input.type === "email") {
|
||||
if (
|
||||
@@ -901,15 +1029,12 @@ export default defineComponent({
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Log all destination errors at the end if any exist
|
||||
if (destinationErrors.length > 0) {
|
||||
destinationErrorsToDisplay.value.push(destinationErrors);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// If all validations pass
|
||||
return true;
|
||||
};
|
||||
@@ -943,7 +1068,7 @@ export default defineComponent({
|
||||
|
||||
// Emit update after each successful creation
|
||||
emit("update:destinations");
|
||||
|
||||
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
destinationCreators.value.push({
|
||||
@@ -967,6 +1092,7 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
return {
|
||||
store,
|
||||
t,
|
||||
jsonStr,
|
||||
importJson,
|
||||
@@ -1003,6 +1129,10 @@ export default defineComponent({
|
||||
filteredTemplates,
|
||||
userSelectedSkipTlsVerify,
|
||||
updateSkipTlsVerify,
|
||||
userSelectedActionId,
|
||||
filterActions,
|
||||
filteredActions,
|
||||
updateDestinationAction,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
||||
@@ -125,7 +125,20 @@
|
||||
>
|
||||
{{ t("common.duration") + " *" }}
|
||||
</div>
|
||||
<DateTime label="Start Time" class="q-py-xs tw-w-full" />
|
||||
|
||||
<DateTime
|
||||
label="Start Time"
|
||||
class="q-py-xs tw-w-full"
|
||||
auto-apply
|
||||
:default-type="dateTime.type"
|
||||
:default-absolute-time="{
|
||||
startTime: dateTime.startTime,
|
||||
endTime: dateTime.endTime,
|
||||
}"
|
||||
:default-relative-time="dateTime.relativeTimePeriod"
|
||||
data-test="logs-search-bar-date-time-dropdown"
|
||||
@on:date-change="updateDateTime"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -493,6 +506,10 @@ const updateStreams = async (resetStream = true) => {
|
||||
.finally(() => (isFetchingStreams.value = false));
|
||||
};
|
||||
|
||||
const updateDateTime = (value: any) => {
|
||||
dateTime.value = value;
|
||||
};
|
||||
|
||||
const getResults = async () => {
|
||||
loading.value.events = true;
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ const useEnterpriseRoutes = () => {
|
||||
|
||||
if (config.isCloud == "true" || config.isEnterprise == "true") {
|
||||
routes.push({
|
||||
path: "action-scripts",
|
||||
path: "actions",
|
||||
name: "actionScripts",
|
||||
component: ActionScipts,
|
||||
beforeEnter(to: any, from: any, next: any) {
|
||||
|
||||
@@ -684,13 +684,6 @@ export default defineComponent({
|
||||
link: "/alerts",
|
||||
name: "alertList",
|
||||
},
|
||||
{
|
||||
title: t("menu.actions"),
|
||||
icon: outlinedCode,
|
||||
link: "/action-scripts",
|
||||
name: "actionScripts",
|
||||
hide: !isActionsEnabled.value,
|
||||
},
|
||||
{
|
||||
title: t("menu.ingestion"),
|
||||
icon: outlinedFilterAlt,
|
||||
@@ -822,8 +815,9 @@ export default defineComponent({
|
||||
|
||||
store.dispatch("setHiddenMenus", disableMenus);
|
||||
|
||||
linksList.value = linksList.value.filter((link) => {
|
||||
const hide = link.hide === undefined ? false : link.hide; // Handle unknown hide values
|
||||
linksList.value = linksList.value.filter((link: any) => {
|
||||
const hide = link.hide === undefined ? false : link.hide;
|
||||
|
||||
return !disableMenus.has(link.name) && !hide;
|
||||
});
|
||||
};
|
||||
@@ -1141,10 +1135,14 @@ export default defineComponent({
|
||||
linksList.value = mainLayoutMixin
|
||||
.setup()
|
||||
.leftNavigationLinks(linksList, t);
|
||||
filterMenus();
|
||||
}
|
||||
|
||||
store.dispatch("setConfig", res.data);
|
||||
await nextTick();
|
||||
|
||||
updateActionsMenu();
|
||||
|
||||
filterMenus();
|
||||
// if rum enabled then setUser to capture session details.
|
||||
if (res.data.rum.enabled) {
|
||||
setRumUser();
|
||||
@@ -1192,6 +1190,27 @@ export default defineComponent({
|
||||
window.open(slackURL, "_blank");
|
||||
};
|
||||
|
||||
const updateActionsMenu = () => {
|
||||
if (isActionsEnabled.value) {
|
||||
const alertIndex = linksList.value.findIndex(
|
||||
(link) => link.name === "alertList",
|
||||
);
|
||||
|
||||
const actionExists = linksList.value.some(
|
||||
(link) => link.name === "actionScripts",
|
||||
);
|
||||
|
||||
if (alertIndex !== -1 && !actionExists) {
|
||||
linksList.value.splice(alertIndex + 1, 0, {
|
||||
title: t("menu.actions"),
|
||||
icon: outlinedCode,
|
||||
link: "/actions",
|
||||
name: "actionScripts",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
t,
|
||||
router,
|
||||
|
||||
@@ -1124,15 +1124,16 @@
|
||||
"save": "Save",
|
||||
"name": "Action Name",
|
||||
"header": "Actions",
|
||||
"add": "Create Action",
|
||||
"add": "Add Action",
|
||||
"deleteTitle": "Delete",
|
||||
"deleteText": "Are you sure you want to delete file ?",
|
||||
"uploadCodeZip": "Upload Code Zip",
|
||||
"uploadCodeZip": "Upload Code ZIP File",
|
||||
"update":"Update Action",
|
||||
"type":"Execution Type",
|
||||
"serviceAccount":"Service Account",
|
||||
"search": "Search Actions",
|
||||
"timezone": "Timezone"
|
||||
"search": "Search Action",
|
||||
"timezone": "Timezone",
|
||||
"zipFileRequired": "ZIP File is required!"
|
||||
},
|
||||
"cipherKey": {
|
||||
"header": "Cipher Keys",
|
||||
|
||||
Reference in New Issue
Block a user