Merge pull request #16 from sadnub/feature-policies-alerts

More Tests, Finished Task and Check policy tables, many fixes and ref…
This commit is contained in:
wh1te909
2020-05-24 21:08:44 -07:00
committed by GitHub
17 changed files with 607 additions and 678 deletions

View File

@@ -2,7 +2,7 @@
<div style="width: 900px; max-width: 90vw;">
<q-card>
<q-bar>
<q-btn ref="refresh" @click="clearRow" class="q-mr-sm" dense flat push icon="refresh" />Automation Manager
<q-btn ref="refresh" @click="refresh" class="q-mr-sm" dense flat push icon="refresh" />Automation Manager
<q-space />
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
@@ -21,6 +21,30 @@
icon="add"
@click="showAddPolicyModal"
/>
<q-btn
ref="edit"
label="Edit"
:disable="selectedRow === null"
dense
flat
push
unelevated
no-caps
icon="edit"
@click="showEditPolicyModal(selectedRow)"
/>
<q-btn
ref="delete"
label="Delete"
:disable="selectedRow === null"
dense
flat
push
unelevated
no-caps
icon="delete"
@click="deletePolicy(selectedRow)"
/>
<q-btn
ref="overview"
label="Policy Overview"
@@ -30,7 +54,7 @@
unelevated
no-caps
icon="remove_red_eye"
@click="showPolicyOverviewModal = true"
@click="showPolicyOverview"
/>
</div>
<q-table
@@ -106,16 +130,12 @@
<q-td>{{ props.row.desc }}</q-td>
<q-td>{{ props.row.active }}</q-td>
<q-td>
<q-btn
:label="`See Related (${props.row.clients.length + props.row.sites.length + props.row.agents.length}+)`"
color="primary"
dense
flat
unelevated
no-caps
<span
style="cursor:pointer;color:blue;text-decoration:underline"
@click="showRelationsModal(props.row)"
size="sm"
/>
>
{{ `See Related (${props.row.clients.length + props.row.sites.length + props.row.agents.length}+)` }}
</span>
</q-td>
</q-tr>
</template>
@@ -123,29 +143,41 @@
</div>
<q-card-section>
<PolicySubTableTabs :policypk="selectedRow" />
<PolicySubTableTabs />
</q-card-section>
</q-card>
<q-dialog v-model="showPolicyFormModal">
<!-- policy form modal -->
<q-dialog
v-model="showPolicyFormModal"
@hide="closePolicyFormModal"
>
<PolicyForm
:pk="editPolicyId"
@close="closeEditPolicyModal"
@refresh="clearRow" />
@close="closePolicyFormModal"
/>
</q-dialog>
<q-dialog v-model="showPolicyOverviewModal">
<PolicyOverview
@close="showPolicyOverviewModal = false" />
<!-- policy overview modal -->
<q-dialog
v-model="showPolicyOverviewModal"
@hide="clearRow"
>
<PolicyOverview />
</q-dialog>
<q-dialog v-model="showRelationsViewModal">
<RelationsView
:policy="policy"
@close="closeRelationsModal" />
<!-- policy relations modal -->
<q-dialog
v-model="showRelationsViewModal"
@hide="closeRelationsModal"
>
<RelationsView :policy="policy" />
</q-dialog>
</div>
</template>
<script>
import mixins from "@/mixins/mixins";
import mixins, { notifySuccessConfig, notifyErrorConfig } from "@/mixins/mixins";
import { mapState } from "vuex";
import PolicyForm from "@/components/automation/modals/PolicyForm";
import PolicyOverview from "@/components/automation/PolicyOverview";
@@ -208,17 +240,20 @@ export default {
this.$store.dispatch("automation/loadPolicies");
},
policyRowSelected({ added, keys, rows }) {
// First key of the array is the selected row pk
// First item of the keys array is the selected policy pk
this.$store.commit("automation/setSelectedPolicy", keys[0]);
this.$store.dispatch("automation/loadPolicyChecks", keys[0]);
this.$store.dispatch("automation/loadPolicyAutomatedTasks", keys[0]);
},
clearRow() {
this.getPolicies();
this.$store.commit("automation/setSelectedPolicy", null);
this.$store.commit("automation/setPolicyChecks", {});
this.$store.commit("automation/setPolicyAutomatedTasks", {});
},
refresh() {
this.getPolicies();
this.clearRow();
},
deletePolicy(id) {
this.$q
.dialog({
@@ -230,10 +265,10 @@ export default {
this.$store
.dispatch("automation/deletePolicy", id)
.then(response => {
this.notifySuccess(`Policy was deleted!`);
this.$q.notify(notifySuccessConfig("Policy was deleted!"));
})
.catch(error => {
this.notifyError(`An Error occured while deleting policy`);
this.$q.notify(notifyErrorConfig("An Error occured while deleting policy"));
});
});
},
@@ -249,12 +284,17 @@ export default {
this.editPolicyId = id;
this.showPolicyFormModal = true;
},
closeEditPolicyModal() {
closePolicyFormModal() {
this.showPolicyFormModal = false;
this.editPolicyId = null;
this.refresh();
},
showAddPolicyModal() {
this.showPolicyFormModal = true;
},
showPolicyOverview() {
this.showPolicyOverviewModal = true
this.clearRow();
}
},
computed: {
@@ -264,7 +304,7 @@ export default {
})
},
mounted() {
this.clearRow();
this.refresh();
}
};
</script>

View File

@@ -1,120 +0,0 @@
<template>
<div class="row">
<div class="col-12">
<template v-if="tasks === undefined || tasks.length === 0">
<p>No Tasks on this policy</p>
</template>
<template v-else>
<q-table
dense
class="tabs-tbl-sticky"
:data="tasks"
:columns="columns"
:row-key="row => row.id"
binary-state-sort
:pagination.sync="pagination"
hide-bottom
>
<!-- header slots -->
<template v-slot:header-cell-enabled="props">
<q-th auto-width :props="props">
<small>Enabled</small>
</q-th>
</template>
<!-- body slots -->
<template slot="body" slot-scope="props" :props="props">
<q-tr>
<!-- tds -->
<q-td>
<q-checkbox disabled dense v-model="props.row.enabled" />
</q-td>
<q-td>{{ props.row.name }}</q-td>
<q-td v-if="props.row.retcode || props.row.stdout || props.row.stderr">
<span style="cursor:pointer;color:blue;text-decoration:underline">output</span>
</q-td>
<q-td v-else>Awaiting output</q-td>
<q-td v-if="props.row.last_run">{{ props.row.last_run }}</q-td>
<q-td v-else>Has not run yet</q-td>
<q-td>{{ props.row.schedule }}</q-td>
<q-td>{{ props.row.assigned_check }}</q-td>
</q-tr>
</template>
</q-table>
</template>
</div>
</div>
</template>
<script>
import axios from "axios";
import mixins from "@/mixins/mixins";
export default {
name: "OverviewAutomatedTasksTab",
props: ["policypk"],
mixins: [mixins],
data() {
return {
automatedTasks: {},
columns: [
{ name: "enabled", align: "left", field: "enabled" },
{ name: "name", label: "Name", field: "name", align: "left" },
{
name: "moreinfo",
label: "More Info",
field: "more_info",
align: "left"
},
{
name: "datetime",
label: "Last Run Time",
field: "last_run",
align: "left"
},
{
name: "schedule",
label: "Schedule",
field: "schedule",
align: "left"
},
{
name: "assignedcheck",
label: "Assigned Check",
field: "assigned_check",
align: "left"
}
],
pagination: {
rowsPerPage: 9999
}
};
},
mounted() {
this.getPolicyTasks();
},
watch: {
policypk: function() {
this.getPolicyTasks();
}
},
methods: {
getPolicyTasks() {
axios
.get(`/automation/${this.policypk}/policyautomatedtasks/`)
.then(r => {
this.automatedTasks = r.data;
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data);
});
}
},
computed: {
tasks() {
return this.automatedTasks.autotasks;
}
}
};
</script>

View File

@@ -1,168 +0,0 @@
<template>
<div class="row">
<div class="col-12">
<template v-if="Object.keys(checks).length === 0 || allChecks.length === 0">
<p>No checks on this policy</p>
</template>
<template v-else>
<q-table
dense
class="tabs-tbl-sticky"
:data="allChecks"
:columns="columns"
:row-key="row => row.id + row.check_type"
binary-state-sort
:pagination.sync="pagination"
hide-bottom
>
<!-- header slots -->
<template v-slot:header-cell-smsalert="props">
<q-th auto-width :props="props">
<q-icon name="phone_android" size="1.5em">
<q-tooltip>SMS Alert</q-tooltip>
</q-icon>
</q-th>
</template>
<template v-slot:header-cell-emailalert="props">
<q-th auto-width :props="props">
<q-icon name="email" size="1.5em">
<q-tooltip>Email Alert</q-tooltip>
</q-icon>
</q-th>
</template>
<template v-slot:header-cell-statusicon="props">
<q-th auto-width :props="props"></q-th>
</template>
<!-- body slots -->
<template slot="body" slot-scope="props" :props="props">
<q-tr>
<!-- tds -->
<q-td>
<q-checkbox disabled dense v-model="props.row.text_alert" />
</q-td>
<q-td>
<q-checkbox disabled dense v-model="props.row.email_alert" />
</q-td>
<q-td v-if="props.row.status === 'pending'"></q-td>
<q-td v-else-if="props.row.status === 'passing'">
<q-icon style="font-size: 1.3rem;" color="positive" name="check_circle" />
</q-td>
<q-td v-else-if="props.row.status === 'failing'">
<q-icon style="font-size: 1.3rem;" color="negative" name="error" />
</q-td>
<q-td
v-if="props.row.check_type === 'diskspace'"
>Disk Space Drive {{ props.row.disk }} > {{props.row.threshold }}%</q-td>
<q-td
v-else-if="props.row.check_type === 'cpuload'"
>Avg CPU Load > {{ props.row.cpuload }}%</q-td>
<q-td
v-else-if="props.row.check_type === 'script'"
>Script check: {{ props.row.script.name }}</q-td>
<q-td
v-else-if="props.row.check_type === 'ping'"
>Ping {{ props.row.name }} ({{ props.row.ip }})</q-td>
<q-td
v-else-if="props.row.check_type === 'memory'"
>Avg memory usage > {{ props.row.threshold }}%</q-td>
<q-td
v-else-if="props.row.check_type === 'winsvc'"
>Service Check - {{ props.row.svc_display_name }}</q-td>
<q-td
v-else-if="props.row.check_type === 'eventlog'"
>Event Log Check - {{ props.row.desc }}</q-td>
<q-td v-if="props.row.status === 'pending'">Awaiting First Synchronization</q-td>
<q-td v-else-if="props.row.status === 'passing'">
<q-badge color="positive">Passing</q-badge>
</q-td>
<q-td v-else-if="props.row.status === 'failing'">
<q-badge color="negative">Failing</q-badge>
</q-td>
<q-td v-if="props.row.check_type === 'ping'">
<span style="cursor:pointer;color:blue;text-decoration:underline">output</span>
</q-td>
<q-td v-else-if="props.row.check_type === 'script'">
<span style="cursor:pointer;color:blue;text-decoration:underline">output</span>
</q-td>
<q-td v-else>{{ props.row.more_info }}</q-td>
<q-td>{{ props.row.last_run }}</q-td>
</q-tr>
</template>
</q-table>
</template>
</div>
</div>
</template>
<script>
import axios from "axios";
import mixins from "@/mixins/mixins";
export default {
name: "OverviewChecksTab",
props: ["policypk"],
mixins: [mixins],
data() {
return {
checks: {},
columns: [
{ name: "smsalert", field: "text_alert", align: "left" },
{ name: "emailalert", field: "email_alert", align: "left" },
{ name: "statusicon", align: "left" },
{ name: "desc", label: "Description", align: "left" },
{ name: "status", label: "Status", field: "status", align: "left" },
{
name: "moreinfo",
label: "More Info",
field: "more_info",
align: "left"
},
{
name: "datetime",
label: "Date / Time",
field: "last_run",
align: "left"
}
],
pagination: {
rowsPerPage: 9999
}
};
},
mounted() {
this.getPolicyChecks();
},
watch: {
policypk: function() {
this.getPolicyChecks();
}
},
methods: {
getPolicyChecks() {
axios
.get(`/checks/${this.policypk}/loadpolicychecks/`)
.then(r => {
this.checks = r.data;
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data);
});
}
},
computed: {
allChecks() {
return [
...this.checks.diskchecks,
...this.checks.cpuloadchecks,
...this.checks.memchecks,
...this.checks.scriptchecks,
...this.checks.winservicechecks,
...this.checks.pingchecks,
...this.checks.eventlogchecks
];
}
}
};
</script>

View File

@@ -10,7 +10,7 @@
text-color="black"
@click="showAddAutomatedTask = true"
/>
<q-btn dense flat push @click="refreshTasks(automatedTasks.pk)" icon="refresh" />
<q-btn dense flat push @click="refreshTasks(automatedTasks.id)" icon="refresh" />
<template v-if="tasks === undefined || tasks.length === 0">
<p>No Tasks</p>
</template>
@@ -70,13 +70,12 @@
/>
</q-td>
<q-td>{{ props.row.name }}</q-td>
<q-td v-if="props.row.retcode || props.row.stdout || props.row.stderr">
<q-td>
<span
style="cursor:pointer;color:blue;text-decoration:underline"
@click="scriptMoreInfo(props.row)"
>output</span>
@click="showStatus(props.row)"
>See Status</span>
</q-td>
<q-td v-else>Awaiting output</q-td>
<q-td v-if="props.row.last_run">{{ props.row.last_run }}</q-td>
<q-td v-else>Has not run yet</q-td>
<q-td>{{ props.row.schedule }}</q-td>
@@ -88,39 +87,48 @@
</div>
<!-- modals -->
<q-dialog v-model="showAddAutomatedTask" position="top">
<AddAutomatedTask @close="showAddAutomatedTask = false" />
<AddAutomatedTask
:policypk="automatedTasks.id"
@close="showAddAutomatedTask = false"
/>
</q-dialog>
<q-dialog v-model="showScriptOutput">
<ScriptOutput @close="showScriptOutput = false; scriptInfo = {}" :scriptInfo="scriptInfo" />
<!-- policy task status -->
<q-dialog v-model="showPolicyTaskStatus">
<PolicyStatus
type="task"
:item="statusTask"
:description="`${statusTask.name} Agent Status`"
/>
</q-dialog>
</div>
</template>
<script>
import axios from "axios";
import { mapState } from "vuex";
import mixins from "@/mixins/mixins";
import mixins, { notifySuccessConfig, notifyErrorConfig } from "@/mixins/mixins";
import AddAutomatedTask from "@/components/modals/tasks/AddAutomatedTask";
import ScriptOutput from "@/components/modals/checks/ScriptOutput";
import PolicyStatus from "@/components/automation/modals/PolicyStatus";
export default {
name: "PolicyAutomatedTasksTab",
components: { AddAutomatedTask, ScriptOutput },
components: {
AddAutomatedTask,
PolicyStatus
},
mixins: [mixins],
data() {
return {
showAddAutomatedTask: false,
showEditAutomatedTask: false,
showScriptOutput: false,
showPolicyTaskStatus: false,
statusTask: {},
editTaskPk: null,
showScriptOutput: false,
scriptInfo: {},
columns: [
{ name: "enabled", align: "left", field: "enabled" },
{ name: "name", label: "Name", field: "name", align: "left" },
{
name: "moreinfo",
name: "status",
label: "More Info",
field: "more_info",
align: "left"
@@ -151,31 +159,32 @@ export default {
},
methods: {
taskEnableorDisable(pk, action) {
const data = { enableordisable: action };
axios
.patch(`/tasks/${pk}/automatedtasks/`, data)
const data = { id: pk, enableordisable: action };
this.$store
.dispatch("editAutoTask", data)
.then(r => {
this.$store.dispatch("automation/loadPolicyAutomatedTasks", this.automatedTasks.pk);
this.notifySuccess(r.data);
this.$store.dispatch("automation/loadPolicyAutomatedTasks", this.automatedTasks.id
);
this.$q.notify(notifySuccessConfig(r.data));
})
.catch(e => this.notifyError("Something went wrong"));
.catch(e => this.$q.notify(notifySuccessConfig("Something went wrong")));
},
showStatus(task) {
this.statusTask = task;
this.showPolicyTaskStatus = true
},
refreshTasks(id) {
this.$store.dispatch("automation/loadPolicyAutomatedTasks", id);
},
scriptMoreInfo(props) {
this.scriptInfo = props;
this.showScriptOutput = true;
},
runTask(pk, enabled) {
if (!enabled) {
this.notifyError("Task cannot be run when it's disabled. Enable it first.");
this.$q.notify(notifyErrorConfig("Task cannot be run when it's disabled. Enable it first."));
return;
}
axios
.get(`/automation/runwintask/${pk}/`)
.then(r => this.notifySuccess(r.data))
.catch(() => this.notifyError("Something went wrong"));
this.$store
.dispatch("automation/runPolicyTask", pk)
.then(r => this.$q.notify(notifySuccessConfig(r.data)))
.catch(() => this.$q.notify(notifyErrorConfig("Something went wrong")));
},
deleteTask(name, pk) {
this.$q
@@ -186,13 +195,13 @@ export default {
persistent: true
})
.onOk(() => {
axios
.delete(`/automation/${pk}/automatedtasks/`)
this.$store
.dispatch("deleteAutoTask", pk)
.then(r => {
this.$store.dispatch("automation/loadPolicyChecks", this.automatedTasks.pk);
this.notifySuccess(r.data);
this.$store.dispatch("automation/loadPolicyAutomatedTasks", this.automatedTasks.id);
this.$q.notify(notifySuccessConfig(r.data));
})
.catch(e => this.notifyError("Something went wrong"));
.catch(e => this.$q.notify(notifyErrorConfig("Something went wrong")));
});
}
},

View File

@@ -2,46 +2,52 @@
<div v-if="Object.keys(checks).length === 0">No Policy Selected</div>
<div class="row" v-else>
<div class="col-12">
<q-btn size="sm" color="grey-5" icon="fas fa-plus" label="Add Check" text-color="black">
<q-btn
size="sm"
color="grey-5"
icon="fas fa-plus"
label="Add Check"
text-color="black"
>
<q-menu>
<q-list dense style="min-width: 200px">
<q-item clickable v-close-popup @click="showAddDiskSpaceCheck = true">
<q-item clickable v-close-popup @click="showAddDialog('AddDiskSpaceCheck')">
<q-item-section side>
<q-icon size="xs" name="far fa-hdd" />
</q-item-section>
<q-item-section>Disk Space Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddPingCheck = true">
<q-item clickable v-close-popup @click="showAddDialog('AddPingCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-network-wired" />
</q-item-section>
<q-item-section>Ping Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddCpuLoadCheck = true">
<q-item clickable v-close-popup @click="showAddDialog('AddCpuLoadCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-microchip" />
</q-item-section>
<q-item-section>CPU Load Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddMemCheck = true">
<q-item clickable v-close-popup @click="showAddDialog('AddMemCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-memory" />
</q-item-section>
<q-item-section>Memory Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddWinSvcCheck = true">
<q-item clickable v-close-popup @click="showAddDialog('AddWinSvcCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-cogs" />
</q-item-section>
<q-item-section>Windows Service Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddScriptCheck = true">
<q-item clickable v-close-popup @click="showAddDialog('AddScriptCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-terminal" />
</q-item-section>
<q-item-section>Script Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddEventLogCheck = true">
<q-item clickable v-close-popup @click="showAddDialog('AddEventLogCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-clipboard-list" />
</q-item-section>
@@ -50,7 +56,13 @@
</q-list>
</q-menu>
</q-btn>
<q-btn dense flat push @click="onRefresh(checks.id)" icon="refresh" />
<q-btn
dense
flat
push
@click="onRefresh(checks.id)"
icon="refresh"
/>
<template v-if="allChecks === undefined || allChecks.length === 0">
<p>No Checks</p>
</template>
@@ -84,12 +96,16 @@
<q-th auto-width :props="props"></q-th>
</template>
<!-- body slots -->
<template slot="body" slot-scope="props" :props="props">
<q-tr @contextmenu="editCheckPK = props.row.id">
<template v-slot:body="props">
<q-tr @contextmenu="editCheckPK = props.row.id" :props="props">
<!-- context menu -->
<q-menu context-menu>
<q-list dense style="min-width: 200px">
<q-item clickable v-close-popup @click="editCheck(props.row.check_type)">
<q-item
clickable
v-close-popup
@click="showEditDialog(props.row.check_type)"
>
<q-item-section side>
<q-icon name="edit" />
</q-item-section>
@@ -110,9 +126,9 @@
<q-item clickable v-close-popup @click="showPolicyCheckStatusModal(props.row)">
<q-item-section side>
<q-icon name="remove_red_eye" />
<q-icon name="sync" />
</q-item-section>
<q-item-section>Status</q-item-section>
<q-item-section>Policy Status</q-item-section>
</q-item>
<q-separator></q-separator>
@@ -137,182 +153,58 @@
v-model="props.row.email_alert"
/>
</q-td>
<q-td
v-if="props.row.check_type === 'diskspace'"
>Disk Space Drive {{ props.row.disk }} > {{props.row.threshold }}%</q-td>
<q-td
v-else-if="props.row.check_type === 'cpuload'"
>Avg CPU Load > {{ props.row.cpuload }}%</q-td>
<q-td
v-else-if="props.row.check_type === 'script'"
>Script check: {{ props.row.script.name }}</q-td>
<q-td
v-else-if="props.row.check_type === 'ping'"
>Ping {{ props.row.name }} ({{ props.row.ip }})</q-td>
<q-td
v-else-if="props.row.check_type === 'memory'"
>Avg memory usage > {{ props.row.threshold }}%</q-td>
<q-td
v-else-if="props.row.check_type === 'winsvc'"
>Service Check - {{ props.row.svc_display_name }}</q-td>
<q-td
v-else-if="props.row.check_type === 'eventlog'"
>Event Log Check - {{ props.row.desc }}</q-td>
<q-td>{{ getDescription(props.row) }}</q-td>
<q-td>
<q-btn
label="See Status"
color="primary"
dense
flat
unelevated
no-caps
<span
style="cursor:pointer;color:blue;text-decoration:underline"
@click="showPolicyCheckStatusModal(props.row)"
size="sm"
/>
>
See Status
</span>
</q-td>
</q-tr>
</template>
</q-table>
</template>
</div>
<!-- modals -->
<q-dialog v-model="showAddDiskSpaceCheck">
<AddDiskSpaceCheck @close="showAddDiskSpaceCheck = false" :policypk="checks.id" />
</q-dialog>
<q-dialog v-model="showEditDiskSpaceCheck">
<EditDiskSpaceCheck
@close="showEditDiskSpaceCheck = false"
:editCheckPK="editCheckPK"
:policypk="checks.id"
/>
</q-dialog>
<q-dialog v-model="showAddPingCheck">
<AddPingCheck @close="showAddPingCheck = false" :policypk="checks.id" />
</q-dialog>
<q-dialog v-model="showEditPingCheck">
<EditPingCheck
@close="showEditPingCheck = false"
:editCheckPK="editCheckPK"
:policypk="checks.id"
/>
</q-dialog>
<q-dialog v-model="showAddCpuLoadCheck">
<AddCpuLoadCheck @close="showAddCpuLoadCheck = false" :policypk="checks.id" />
</q-dialog>
<q-dialog v-model="showEditCpuLoadCheck">
<EditCpuLoadCheck
@close="showEditCpuLoadCheck = false"
:editCheckPK="editCheckPK"
:policypk="checks.id"
/>
</q-dialog>
<q-dialog v-model="showAddMemCheck">
<AddMemCheck @close="showAddMemCheck = false" :policypk="checks.id" />
</q-dialog>
<q-dialog v-model="showEditMemCheck">
<EditMemCheck
@close="showEditMemCheck = false"
:editCheckPK="editCheckPK"
:policypk="checks.id"
/>
</q-dialog>
<q-dialog v-model="showAddWinSvcCheck">
<AddWinSvcCheck @close="showAddWinSvcCheck = false" :policypk="checks.id" />
</q-dialog>
<q-dialog v-model="showEditWinSvcCheck">
<EditWinSvcCheck
@close="showEditWinSvcCheck = false"
:editCheckPK="editCheckPK"
:policypk="checks.id"
/>
</q-dialog>
<!-- script check -->
<q-dialog v-model="showAddScriptCheck">
<AddScriptCheck @close="showAddScriptCheck = false" :policypk="checks.id" />
</q-dialog>
<q-dialog v-model="showEditScriptCheck">
<EditScriptCheck
@close="showEditScriptCheck = false"
:editCheckPK="editCheckPK"
:policypk="checks.id"
/>
</q-dialog>
<!-- event log check -->
<q-dialog v-model="showAddEventLogCheck">
<AddEventLogCheck @close="showAddEventLogCheck = false" :policypk="checks.id" />
</q-dialog>
<q-dialog v-model="showEditEventLogCheck">
<EditEventLogCheck
@close="showEditEventLogCheck = false"
:editCheckPK="editCheckPK"
:policypk="checks.id"
/>
</q-dialog>
<!-- policy status -->
<q-dialog v-model="showPolicyCheckStatus">
<PolicyCheckStatus :check="statusCheck" @close="closePolicyCheckStatusModal" />
<PolicyStatus
type="check"
:item="statusCheck"
:description="getDescription(statusCheck)"
/>
</q-dialog>
<!-- add/edit modals -->
<q-dialog v-model="showDialog">
<component
:is="dialogComponent"
@close="hideDialog"
:policypk="checks.id"
:editCheckPK="editCheckPK"
/>
</q-dialog>
</div>
</template>
<script>
import axios from "axios";
import { mapState } from "vuex";
import mixins from "@/mixins/mixins";
import AddDiskSpaceCheck from "@/components/modals/checks/AddDiskSpaceCheck";
import EditDiskSpaceCheck from "@/components/modals/checks/EditDiskSpaceCheck";
import AddPingCheck from "@/components/modals/checks/AddPingCheck";
import EditPingCheck from "@/components/modals/checks/EditPingCheck";
import AddCpuLoadCheck from "@/components/modals/checks/AddCpuLoadCheck";
import EditCpuLoadCheck from "@/components/modals/checks/EditCpuLoadCheck";
import AddMemCheck from "@/components/modals/checks/AddMemCheck";
import EditMemCheck from "@/components/modals/checks/EditMemCheck";
import AddWinSvcCheck from "@/components/modals/checks/AddWinSvcCheck";
import EditWinSvcCheck from "@/components/modals/checks/EditWinSvcCheck";
import AddScriptCheck from "@/components/modals/checks/AddScriptCheck";
import EditScriptCheck from "@/components/modals/checks/EditScriptCheck";
import PolicyCheckStatus from "@/components/automation/modals/PolicyCheckStatus";
import AddEventLogCheck from "@/components/modals/checks/AddEventLogCheck";
import EditEventLogCheck from "@/components/modals/checks/EditEventLogCheck";
import { mapState, mapGetters } from "vuex";
import mixins, { notifySuccessConfig, notifyErrorConfig } from "@/mixins/mixins";
import PolicyStatus from "@/components/automation/modals/PolicyStatus";
export default {
name: "PolicyChecksTab",
props: ["policypk"],
components: {
AddDiskSpaceCheck,
EditDiskSpaceCheck,
AddPingCheck,
EditPingCheck,
AddCpuLoadCheck,
EditCpuLoadCheck,
AddMemCheck,
EditMemCheck,
AddWinSvcCheck,
EditWinSvcCheck,
AddScriptCheck,
EditScriptCheck,
PolicyCheckStatus,
AddEventLogCheck,
EditEventLogCheck
PolicyStatus
},
mixins: [mixins],
data() {
return {
showAddDiskSpaceCheck: false,
showEditDiskSpaceCheck: false,
showAddPingCheck: false,
showEditPingCheck: false,
showAddCpuLoadCheck: false,
showEditCpuLoadCheck: false,
showAddMemCheck: false,
showEditMemCheck: false,
showAddWinSvcCheck: false,
showEditWinSvcCheck: false,
showAddScriptCheck: false,
showEditScriptCheck: false,
dialogComponent: null,
showDialog: false,
showPolicyCheckStatus: false,
showAddEventLogCheck: false,
showEditEventLogCheck: false,
editCheckPK: null,
statusCheck: {},
columns: [
@@ -336,43 +228,58 @@ export default {
action: action
};
const alertColor = alert_action ? "positive" : "warning";
axios.patch("/checks/checkalert/", data).then(r => {
this.$q.notify({
color: alertColor,
icon: "fas fa-check-circle",
message: `${alert_type} alerts ${action}`
this.$store
.dispatch("editCheckAlertAction", data)
.then(r => {
this.$q.notify({
color: alertColor,
icon: "fas fa-check-circle",
message: `${alert_type} alerts ${action}`
});
});
});
},
onRefresh(id) {
this.$store.dispatch("automation/loadPolicyChecks", id);
},
editCheck(category) {
showAddDialog(component) {
this.dialogComponent = () => import(`@/components/modals/checks/${component}`);
this.showDialog = true;
},
showEditDialog(category) {
let component = null;
switch (category) {
case "diskspace":
this.showEditDiskSpaceCheck = true;
component = "EditDiskSpaceCheck";
break;
case "ping":
this.showEditPingCheck = true;
component = "EditPingCheck";
break;
case "cpuload":
this.showEditCpuLoadCheck = true;
tcomponent = "EditCpuLoadCheck";
break;
case "memory":
this.showEditMemCheck = true;
component = "EditMemCheck";
break;
case "winsvc":
this.showEditWinSvcCheck = true;
component = "EditWinSvcCheck";
break;
case "script":
this.showEditScriptCheck = true;
component = "EditScriptCheck";
break;
case "eventlog":
this.showEditEventLogCheck = true;
component = "EditEventLogCheck";
break;
default:
return false;
return null;
}
this.dialogComponent = () => import(`@/components/modals/checks/${component}`);
this.showDialog = true;
},
hideDialog(component) {
this.showDialog = false;
this.dialogComponent = null;
},
deleteCheck(pk, check_type) {
this.$q
@@ -384,13 +291,13 @@ export default {
})
.onOk(() => {
const data = { pk: pk, checktype: check_type };
axios
.delete("checks/deletestandardcheck/", { data: data })
this.$store
.dispatch("deleteCheck", data)
.then(r => {
this.$store.dispatch("automation/loadPolicyChecks", this.policypk);
this.notifySuccess("Check was deleted!");
this.$store.dispatch("automation/loadPolicyChecks", this.checks.id);
this.$q.notify(notifySuccessConfig);
})
.catch(e => this.notifyError(e.response.data.error));
.catch(e => this.$q.notify(notifyErrorConfig));
});
},
showPolicyCheckStatusModal(check) {
@@ -400,23 +307,31 @@ export default {
closePolicyCheckStatusModal() {
this.showPolicyCheckStatus = false;
this.statusCheck = {};
},
getDescription(check) {
if (check.check_type === "diskspace")
return `Disk Space Drive ${check.disk} > ${check.threshold}%`;
else if (check.check_type === "cpuload")
return `Avg CPU Load > ${check.cpuload}%`;
else if (check.check_type === "script")
return `Script check: ${check.script.name}`;
else if (check.check_type === "ping")
return `Ping ${check.name} (${check.ip})`;
else if (check.check_type === "memory")
return `Avg memory usage > ${check.threshold}%`;
else if (check.check_type === "winsvc")
return `Service Check - ${check.svc_display_name}`;
else if (check.check_type === "eventlog")
return `Event Log Check - ${check.desc}`
}
},
computed: {
...mapState({
checks: state => state.automation.checks
}),
allChecks() {
return [
...this.checks.diskchecks,
...this.checks.cpuloadchecks,
...this.checks.memchecks,
...this.checks.scriptchecks,
...this.checks.winservicechecks,
...this.checks.pingchecks,
...this.checks.eventlogchecks
];
}
...mapGetters({
allChecks: "automation/allChecks"
})
}
};
</script>

View File

@@ -11,7 +11,7 @@
<template v-slot:before>
<div class="q-pa-md">
<q-tree
ref="Tree"
ref="tree"
:nodes="clientSiteTree"
node-key="id"
:selected.sync="selected"
@@ -44,16 +44,10 @@
transition-next="jump-up"
>
<q-tab-panel name="checks">
<template v-if="Object.keys(selectedPolicy).length === 0">
<p>Select a Policy</p>
</template>
<OverviewChecksTab v-else :policypk="policypk" />
<PolicyChecksTab :readonly="true" />
</q-tab-panel>
<q-tab-panel name="tasks">
<template v-if="Object.keys(selectedPolicy).length === 0">
<p>Select a Policy</p>
</template>
<OverviewAutomatedTasksTab v-else :policypk="policypk" />
<PolicyAutomatedTasksTab/>
</q-tab-panel>
</q-tab-panels>
</template>
@@ -62,16 +56,15 @@
</template>
<script>
import axios from "axios";
import mixins from "@/mixins/mixins";
import OverviewChecksTab from "@/components/automation/OverviewChecksTab";
import OverviewAutomatedTasksTab from "@/components/automation/OverviewAutomatedTasksTab";
import mixins, { notifyErrorConfig } from "@/mixins/mixins";
import PolicyChecksTab from "@/components/automation/PolicyChecksTab";
import PolicyAutomatedTasksTab from "@/components/automation/PolicyAutomatedTasksTab";
export default {
name: "PolicyOverview",
components: {
OverviewChecksTab,
OverviewAutomatedTasksTab
PolicyAutomatedTasksTab,
PolicyChecksTab
},
mixins: [mixins],
data() {
@@ -83,26 +76,28 @@ export default {
clientSiteTree: []
};
},
mounted() {
this.getPolicyTree();
},
methods: {
getPolicyTree() {
axios
.get(`/automation/policies/overview/`)
this.$store
.dispatch("automation/loadPolicyTreeData")
.then(r => {
this.processTreeDataFromApi(r.data);
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data);
this.$q.notify(notifyErrorConfig(e.response.data));
});
},
loadPolicyDetails(key) {
if (key === undefined) {
if (key === undefined || key === null) {
return;
}
this.selectedPolicy = this.$refs.Tree.getNodeByKey(key);
this.selectedPolicy = this.$refs.tree.getNodeByKey(key);
this.$store.dispatch("automation/loadPolicyChecks", this.selectedPolicy.id);
this.$store.dispatch("automation/loadPolicyAutomatedTasks", this.selectedPolicy.id);
},
processTreeDataFromApi(data) {
/* Structure
@@ -110,12 +105,14 @@ export default {
* "client_name_1": {
* "policies": [
* {
* id: 1
* id: 1,
* name: "Policy Name 1"
* }
* ]
* "site_name_1": {
* "policies": []
* ],
* sites: {
* "site_name_1": {
* "policies": []
* }
* }
* }
* }]
@@ -138,13 +135,22 @@ export default {
unique_id--;
// Add any policies assigned to client
if (data[client].policies.length > 0) {
for (let policy in data[client].policies)
for (let policy in data[client].policies) {
let disabled = "";
// Indicate if the policy is active or not
if (!data[client].policies[policy].active) {
disabled = " (disabled)";
}
client_temp["children"].push({
label: data[client].policies[policy].name,
label: data[client].policies[policy].name + disabled,
icon: "policy",
id: data[client].policies[policy].id
});
}
}
// Iterate through Sites
@@ -162,8 +168,15 @@ export default {
site_temp["children"] = [];
for (let policy in data[client].sites[site].policies) {
// Indicate if the policy is active or not
let disabled = "";
if (!data[client].sites[site].policies[policy].active) {
disabled = " (disabled)";
}
site_temp["children"].push({
label: data[client].sites[site].policies[policy].name,
label: data[client].sites[site].policies[policy].name + disabled,
icon: "policy",
id: data[client].sites[site].policies[policy].id
});
@@ -181,10 +194,8 @@ export default {
this.clientSiteTree = result;
}
},
computed: {
policypk() {
return this.selectedPolicy.id;
}
}
mounted() {
this.getPolicyTree();
},
};
</script>
</script>

View File

@@ -17,7 +17,7 @@
<q-separator />
<q-tab-panels v-model="subtab" :animated="false">
<q-tab-panel name="checks">
<PolicyChecksTab :policypk="policypk" />
<PolicyChecksTab />
</q-tab-panel>
<q-tab-panel name="tasks">
<PolicyAutomatedTasksTab />
@@ -32,7 +32,6 @@ import PolicyAutomatedTasksTab from "@/components/automation/PolicyAutomatedTask
export default {
name: "PolicySubTableTabs",
props: ["policypk"],
components: {
PolicyChecksTab,
PolicyAutomatedTasksTab

View File

@@ -1,33 +0,0 @@
<template>
<q-card style="width: 60vw" >
<q-card-section class="row items-center">
<div class="text-h6">Policy Name Status</div>
<div class="text-subtitle1">{{ this.check.desc }}</div>
<q-space />
<q-btn icon="close" flat round dense v-close-popup />
</q-card-section>
<q-card-section>
<q-list bordered separator>
<q-item>
<q-item-section>Single line item</q-item-section>
</q-item>
</q-list>
</q-card-section>
</q-card>
</template>
<script>
export default {
name: "PolicyCheckStatus",
props: {
check: {
required: true,
type: Object
}
},
data() {
return {
}
},
}
</script>

View File

@@ -98,7 +98,7 @@
</template>
<script>
import mixins from "@/mixins/mixins";
import mixins, { notifySuccessConfig, notifyErrorConfig } from "@/mixins/mixins";
import dropdown_formatter from "@/mixins/dropdown_formatter";
export default {
@@ -146,7 +146,7 @@ export default {
},
submit() {
if (!this.name) {
this.notifyError("Name is required!");
this.$q.notify(notifySuccessConfig("Name is required!"));
return false;
}
@@ -167,12 +167,11 @@ export default {
.then(r => {
this.$q.loading.hide();
this.$emit("close");
this.$emit("refresh");
this.notifySuccess("Policy edited!");
this.$q.notify(notifySuccessConfig("Policy edited!"));
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data);
this.$q.notify(notifyErrorConfig(e.response.data));
});
} else {
this.$store
@@ -180,12 +179,11 @@ export default {
.then(r => {
this.$q.loading.hide();
this.$emit("close");
this.$emit("refresh");
this.notifySuccess("Policy added! Now you can add Tasks and Checks!");
this.$q.notify(notifySuccessConfig("Policy added! Now you can add Tasks and Checks!"));
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data);
this.$q.notify(notifyErrorConfig(e.response.data));
});
}
},

View File

@@ -0,0 +1,59 @@
<template>
<q-card style="width: 60vw" >
<q-card-section class="row items-center">
<div class="text-h6">{{ this.description }}</div>
<q-space />
<q-btn icon="close" flat round dense v-close-popup />
</q-card-section>
<q-card-section>
<q-list bordered separator>
<q-item>
<q-item-section>Single line item</q-item-section>
</q-item>
</q-list>
</q-card-section>
</q-card>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "PolicyStatus",
props: {
item: {
required: true,
type: Object
},
type: {
required: true,
type: String,
validator: function (value) {
// The value must match one of these strings
return ["task", "check"].includes(value);
}
},
description: {
type: String
}
},
data() {
return {
checkData: []
}
},
methods: {
getCheckData() {
}
},
computed: {
...mapState({
selectedPolicyId: state => state.automation.selectedPolicyId
}),
},
mounted() {
this.getCheckData();
}
}
</script>

View File

@@ -108,12 +108,14 @@
<script>
import axios from "axios";
import { mapState } from "vuex";
import { mapGetters } from "vuex";
import { mapState, mapGetters } from "vuex";
import mixins from "@/mixins/mixins";
export default {
name: "AddAutomatedTask",
props: {
policypk: Number
},
mixins: [mixins],
data() {
return {
@@ -141,8 +143,10 @@ export default {
if (!this.step1Done || !this.step2Done) {
this.notifyError("Some steps incomplete");
} else {
const pk = this.policypk ? { policy: this.policypk } : { agent: this.selectedAgentPk };
const data = {
agent: this.selectedAgentPk,
...pk,
name: this.taskName,
script: this.scriptPk,
trigger: this.trigger,
@@ -151,12 +155,20 @@ export default {
days: this.days,
timeout: this.timeout
};
console.log(data)
axios
.post(`/tasks/${this.selectedAgentPk}/automatedtasks/`, data)
.post("tasks/automatedtasks/", data)
.then(r => {
this.$emit("close");
this.$store.dispatch("loadAutomatedTasks", this.selectedAgentPk);
this.$store.dispatch("loadChecks", this.selectedAgentPk);
if (!this.policypk) {
this.$store.dispatch("loadAutomatedTasks", this.selectedAgentPk);
this.$store.dispatch("loadChecks", this.selectedAgentPk);
} else {
this.$store.dispatch("automation/loadPolicyAutomatedTasks", this.policypk);
this.$store.dispatch("automation/loadPolicyChecks", this.policypk);
}
this.notifySuccess(r.data);
})
.catch(e => this.notifyError(e.response.data));
@@ -169,7 +181,7 @@ export default {
computed: {
...mapGetters(["selectedAgentPk", "scripts"]),
...mapState({
checks: state => state.agentChecks
checks: state => this.policypk ? state.automation.checks : state.agentChecks
}),
allChecks() {
return [

View File

@@ -1,5 +1,37 @@
import { Notify } from "quasar";
export function notifySuccessConfig(msg, timeout = 2000) {
return {
type: "positive",
message: msg,
timeout: timeout
}
};
export function notifyErrorConfig(msg, timeout = 2000) {
return {
type: "negative",
message: msg,
timeout: timeout
}
};
export function notifyWarningConfig(msg, timeout = 2000) {
return {
type: "warning",
message: msg,
timeout: timeout
}
};
export function notifyInfoConfig(msg, timeout = 2000) {
return {
type: "info",
message: msg,
timeout: timeout
}
};
export default {
methods: {
bootTime(unixtime) {
@@ -26,32 +58,16 @@ export default {
}
},
notifySuccess(msg, timeout = 2000) {
Notify.create({
type: "positive",
message: msg,
timeout: timeout
});
Notify.create(notifySuccessConfig(msg, timeout));
},
notifyError(msg, timeout = 2000) {
Notify.create({
type: "negative",
message: msg,
timeout: timeout
});
Notify.create(notifyErrorConfig(msg, timeout));
},
notifyWarning(msg, timeout = 2000) {
Notify.create({
type: "warning",
message: msg,
timeout: timeout
});
Notify.create(notifyWarningConfig(msg, timeout));
},
notifyInfo(msg, timeout = 2000) {
Notify.create({
type: "info",
message: msg,
timeout: timeout
});
Notify.create(notifyInfoConfig(msg, timeout));
}
}
};

View File

@@ -10,6 +10,17 @@ export default {
},
getters: {
allChecks(state) {
return [
...state.checks.diskchecks,
...state.checks.cpuloadchecks,
...state.checks.memchecks,
...state.checks.scriptchecks,
...state.checks.winservicechecks,
...state.checks.pingchecks,
...state.checks.eventlogchecks
];
},
selectedPolicyPk(state) {
return state.selectedPolicy;
},
@@ -63,8 +74,14 @@ export default {
context.dispatch("loadPolicies");
});
},
runPolicyTask(context, pk) {
return axios.get(`/automation/runwintask/${pk}/`);
},
getRelated(context, pk) {
return axios.get(`/automation/policies/${pk}/related/`);
},
loadPolicyTreeData(context) {
return axios.get("/automation/policies/overview/");
}
}
}

View File

@@ -144,6 +144,18 @@ export const store = new Vuex.Store({
context.commit("setChecks", r.data);
});
},
editCheckAlertAction(context, data) {
return axios.patch("/checks/checkalert/", data);
},
deleteCheck(context, data) {
return axios.delete("checks/deletestandardcheck/", { data: data });
},
editAutoTask(context, data) {
return axios.patch(`/tasks/${data.id}/automatedtasks/`, data);
},
deleteAutoTask(context, pk) {
return axios.delete(`/tasks/${pk}/automatedtasks/`);
},
getUpdatedSites(context) {
axios.get("/clients/loadclients/").then(r => {
context.commit("getUpdatedSites", r.data);

View File

@@ -74,10 +74,11 @@ describe("AutomationManager.vue", () => {
store,
localVue,
stubs: [
"PolicySubTableTabs",
"PolicySubTableTabs",,
"PolicyOverview",
"PolicyForm",
"RelationsView"
],
]
});
});
@@ -97,6 +98,9 @@ describe("AutomationManager.vue", () => {
it("calls vuex loadPolicies action on mount", () => {
expect(actions.loadPolicies).toHaveBeenCalled();
expect(mutations.setSelectedPolicy).toHaveBeenCalledWith(expect.anything(), null);
expect(mutations.setPolicyChecks).toHaveBeenCalledWith(expect.anything(), {});
expect(mutations.setPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), {});
});
@@ -146,6 +150,38 @@ describe("AutomationManager.vue", () => {
});
it("shows edit policy modal on edit button press", async () => {
const button = wrapper.findComponent({ ref: "edit" });
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
await button.trigger("click")
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
//Select Row
await wrapper.find("tbody > tr.q-tr").trigger("click");
await button.trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
});
it("deletes selected policy on delete button press", async () => {
const button = wrapper.findComponent({ ref: "delete" });
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
// Select Row
await wrapper.find("tbody > tr.q-tr").trigger("click");
await button.trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
//Get OK button and click it
await bodyWrapper.findAll(".q-btn").wrappers[1].trigger("click");
expect(actions.deletePolicy).toHaveBeenCalledWith(expect.anything(), 1);
});
it("deletes selected policy", async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
@@ -170,6 +206,11 @@ describe("AutomationManager.vue", () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
await button.trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(mutations.setSelectedPolicy).toHaveBeenCalledWith(expect.anything(), null);
expect(mutations.setPolicyChecks).toHaveBeenCalledWith(expect.anything(), {});
expect(mutations.setPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), {});
});
it("calls vuex loadPolicies action when refresh button clicked and clears selected", () => {
@@ -199,4 +240,6 @@ describe("AutomationManager.vue", () => {
});
// TODO: Test @close and @hide events
});

View File

@@ -1,8 +1,8 @@
import { mount, createLocalVue, createWrapper } from "@vue/test-utils";
import { mount, createLocalVue } from "@vue/test-utils";
import flushPromises from "flush-promises";
import Vuex from "vuex";
import PolicyForm from "@/components/automation/modals/PolicyForm";
import "@/quasar.js"
import "@/quasar.js";
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -39,22 +39,17 @@ describe("PolicyForm.vue", () => {
const policy = {
id: 1,
name: "Test Policy",
desc: "Test Desc",
active: true,
clients: [],
sites: [],
agents: []
sites: []
};
let methods;
let actions, rootActions, store;
// Runs before every test
beforeEach(() => {
methods = {
notifyError: () => jest.fn()
};
rootActions = {
loadClients: jest.fn(() => new Promise(res => res({ data: clients }))),
loadSites: jest.fn(() => new Promise(res => res({ data: sites }))),
@@ -83,7 +78,7 @@ describe("PolicyForm.vue", () => {
const wrapper = mount(PolicyForm, {
localVue,
store,
store
});
expect(rootActions.loadClients).toHaveBeenCalled();
@@ -106,7 +101,7 @@ describe("PolicyForm.vue", () => {
expect(rootActions.loadClients).toHaveBeenCalled();
expect(rootActions.loadSites).toHaveBeenCalled();
expect(actions.loadPolicy).toHaveBeenCalled();
expect(actions.loadPolicy).toHaveBeenCalledWith(expect.anything(), 1);
});
@@ -129,11 +124,10 @@ describe("PolicyForm.vue", () => {
const wrapper = mount(PolicyForm, {
localVue,
store,
methods: methods
store
});
wrapper.setData({name: "Test Name"});
wrapper.setData({name: "Test Policy"});
const form = wrapper.findComponent({ ref: "form" });
form.vm.$emit("submit");
await wrapper.vm.$nextTick();
@@ -150,17 +144,16 @@ describe("PolicyForm.vue", () => {
store,
propsData: {
pk: 1
},
methods: methods
}
});
wrapper.setData({name: "TestName"})
await flushPromises();
const form = wrapper.findComponent({ ref: "form" });
form.vm.$emit("submit");
await wrapper.vm.$nextTick();
expect(actions.addPolicy).not.toHaveBeenCalled();
expect(actions.editPolicy).toHaveBeenCalled();
expect(actions.editPolicy).toHaveBeenCalledWith(expect.anything(), policy);
});
@@ -168,8 +161,7 @@ describe("PolicyForm.vue", () => {
const wrapper = mount(PolicyForm, {
localVue,
store,
methods: methods
store
});
const form = wrapper.findComponent({ ref: "form" });

View File

@@ -0,0 +1,127 @@
import { mount, createLocalVue } from "@vue/test-utils";
import flushPromises from "flush-promises";
import Vuex from "vuex";
import PolicyOverview from "@/components/automation/PolicyOverview";
import "@/quasar.js";
const localVue = createLocalVue();
localVue.use(Vuex);
describe("PolicyOverview.vue", () => {
const policyTreeData = {
// node 0
"Client Name 1": {
policies: [
{
id: 1,
name: "Policy Name 1",
active: true
}
],
sites: {
// node -1
"Site Name 1": { policies: []}
}
},
// node -2
"Client Name 2": {
policies: [
{
id: 2,
name: "Policy Name 2",
active: true
}
],
sites: {
// node -3
"Site Name 2": {
policies: [
{
id: 3,
name: "Policy Name 3",
active: false
}
]
}
}
}
};
let wrapper, actions, store;
// Runs before every test
beforeEach(() => {
actions = {
loadPolicyTreeData: jest.fn(() => new Promise(res => res({ data: policyTreeData }))),
loadPolicyChecks: jest.fn(),
loadPolicyAutomatedTasks: jest.fn()
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
actions
}
}
});
wrapper = mount(PolicyOverview, {
localVue,
store,
stubs: [
"PolicyChecksTab",
"PolicyAutomatedTasksTab"
]
});
});
// The Tests
it("calls vuex actions on mount", () => {
expect(actions.loadPolicyTreeData).toHaveBeenCalled();
});
it("renders tree data", () => {
const tree = wrapper.findComponent({ ref: "tree" });
const policy1 = tree.vm.getNodeByKey(1);
const policy2 = tree.vm.getNodeByKey(2);
const client1 = tree.vm.getNodeByKey(0);
const client2 = tree.vm.getNodeByKey(-2);
const site1 = tree.vm.getNodeByKey(-1);
const site2 = tree.vm.getNodeByKey(-3);
expect(policy1.label).toBe("Policy Name 1");
expect(policy2.label).toBe("Policy Name 2");
expect(client1.label).toBe("Client Name 1");
expect(client2.label).toBe("Client Name 2");
expect(site1.label).toBe("Site Name 1");
expect(site2.label).toBe("Site Name 2");
});
it("selects tree node and updates data property with policy",() => {
// Expected rendered policy node data
const returnData = {
icon: "policy",
id: 1,
label: "Policy Name 1"
};
// Get second rree node which should be the first policy
wrapper.findAll(".q-tree__node-header").wrappers[1].trigger("click");
expect(wrapper.vm.selectedPolicy).toStrictEqual(returnData);
expect(actions.loadPolicyChecks).toHaveBeenCalledWith(expect.anything(), 1);
expect(actions.loadPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), 1);
});
});