fix(ui): migrate code to mobx v6

This commit is contained in:
Łukasz Mierzwa
2020-10-08 18:56:03 +01:00
committed by Łukasz Mierzwa
parent 3c935aa684
commit e02754016c
2 changed files with 729 additions and 573 deletions

View File

@@ -15,6 +15,7 @@ import {
APIAlertsResponseSilenceMapT,
APIAlertsResponseUpstreamsT,
APIAlertsResponseUpstreamsClusterMapT,
APISettingsT,
} from "Models/APITypes";
const QueryStringEncodeOptions = {
@@ -121,280 +122,358 @@ function NewUnappliedFilter(raw: string): FilterT {
};
}
interface AlertStoreFiltersT {
values: FilterT[];
addFilter: (raw: string) => void;
removeFilter: (raw: string) => void;
replaceFilter: (oldRaw: string, newRaw: string) => void;
setFilters: (raws: string[]) => void;
setFilterValues: (v: FilterT[]) => void;
setWithoutLocation: (raws: string[]) => void;
applyAllFilters: () => void;
}
interface AlertStoreDataT {
colors: APIAlertsResponseColorsT;
counters: APILabelCounterT[];
grids: APIGridT[];
silences: APIAlertsResponseSilenceMapT;
upstreams: APIAlertsResponseUpstreamsT;
receivers: string[];
readonly gridPadding: number;
getAlertmanagerByName: (name: string) => APIAlertmanagerUpstreamT | undefined;
isReadOnlyAlertmanager: (name: string) => boolean;
getClusterAlertmanagersWithoutReadOnly: (clusterID: string) => string[];
readonly readOnlyAlertmanagers: APIAlertmanagerUpstreamT[];
readonly readWriteAlertmanagers: APIAlertmanagerUpstreamT[];
readonly clustersWithoutReadOnly: APIAlertsResponseUpstreamsClusterMapT;
getColorData: (name: string, value: string) => APILabelColorT | undefined;
setGrids: (g: APIGridT[]) => void;
setUpstreams: (u: APIAlertsResponseUpstreamsT) => void;
setInstances: (i: APIAlertmanagerUpstreamT[]) => void;
setClusters: (c: APIAlertsResponseUpstreamsClusterMapT) => void;
}
interface AlertStoreInfoT {
authentication: {
enabled: boolean;
username: string;
};
totalAlerts: number;
version: string;
upgradeReady: boolean;
upgradeNeeded: boolean;
isRetrying: boolean;
reloadNeeded: boolean;
setIsRetrying: () => void;
clearIsRetrying: () => void;
setUpgradeNeeded: () => void;
setReloadNeeded: () => void;
setTotalAlerts: (n: number) => void;
}
interface AlertStoreSettingsT {
values: APISettingsT;
}
interface AlertStoreStatusT {
value: symbol;
lastUpdateAt: number | Date;
error: null | string;
stopped: boolean;
paused: boolean;
setIdle: () => void;
setFetching: () => void;
setProcessing: () => void;
setFailure: (err: string) => void;
pause: () => void;
resume: () => void;
togglePause: () => void;
stop: () => void;
}
class AlertStore {
filters = observable(
{
values: [] as FilterT[],
addFilter(raw: string) {
if (this.values.filter((f) => f.raw === raw).length === 0) {
this.values.push(NewUnappliedFilter(raw));
UpdateLocationSearch({ q: this.values.map((f) => f.raw) });
}
},
removeFilter(raw: string) {
if (this.values.filter((f) => f.raw === raw).length > 0) {
this.values = this.values.filter((f) => f.raw !== raw);
UpdateLocationSearch({ q: this.values.map((f) => f.raw) });
}
},
replaceFilter(oldRaw: string, newRaw: string) {
const index = this.values.findIndex((e) => e.raw === oldRaw);
if (index >= 0) {
// first check if we would create a duplicated filter
if (this.values.findIndex((e) => e.raw === newRaw) >= 0) {
// we already have newRaw, simply drop oldRaw
this.removeFilter(oldRaw);
} else {
// no dups, continue with a swap
this.values[index] = NewUnappliedFilter(newRaw);
UpdateLocationSearch({ q: this.values.map((f) => f.raw) });
}
}
},
setFilters(raws: string[]) {
this.values = raws.map((raw) => NewUnappliedFilter(raw));
UpdateLocationSearch({ q: this.values.map((f) => f.raw) });
},
setFilterValues(v: FilterT[]) {
this.values = v;
},
setWithoutLocation(raws: string[]) {
const filtersByRaw: { [key: string]: FilterT } = this.values.reduce(
function (map: { [key: string]: FilterT }, obj) {
map[toJS(obj.raw)] = toJS(obj);
return map;
},
{}
);
this.values = raws.map((raw) =>
filtersByRaw[raw] ? filtersByRaw[raw] : NewUnappliedFilter(raw)
);
},
applyAllFilters() {
for (let i = 0; i < this.values.length; i++) {
this.values[i].applied = true;
}
},
},
{
addFilter: action.bound,
removeFilter: action.bound,
replaceFilter: action.bound,
setFilters: action.bound,
setFilterValues: action.bound,
setWithoutLocation: action.bound,
applyAllFilters: action.bound,
},
{ name: "API Filters" }
);
data = observable(
{
colors: {} as APIAlertsResponseColorsT,
counters: [] as APILabelCounterT[],
grids: [] as APIGridT[],
silences: {} as APIAlertsResponseSilenceMapT,
upstreams: {
counters: { total: 0, healthy: 0, failed: 0 },
instances: [],
clusters: {},
} as APIAlertsResponseUpstreamsT,
receivers: [] as string[],
get gridPadding(): number {
return this.grids.filter((g) => g.labelName !== "").length > 0 ? 5 : 0;
},
getAlertmanagerByName(
name: string
): APIAlertmanagerUpstreamT | undefined {
return this.upstreams.instances.find((am) => am.name === name);
},
isReadOnlyAlertmanager(name: string): boolean {
return this.readOnlyAlertmanagers.map((am) => am.name).includes(name);
},
getClusterAlertmanagersWithoutReadOnly(clusterID: string): string[] {
return this.clustersWithoutReadOnly[clusterID] || [];
},
get readOnlyAlertmanagers(): APIAlertmanagerUpstreamT[] {
return this.upstreams.instances.filter((am) => am.readonly === true);
},
get readWriteAlertmanagers(): APIAlertmanagerUpstreamT[] {
return this.upstreams.instances
.filter((am) => am.readonly === false)
.map((am) =>
Object.assign({}, am, {
clusterMembers: am.clusterMembers.filter(
(m) => this.isReadOnlyAlertmanager(m) === false
),
})
);
},
get clustersWithoutReadOnly(): APIAlertsResponseUpstreamsClusterMapT {
const clusters: APIAlertsResponseUpstreamsClusterMapT = {};
for (const clusterID of Object.keys(this.upstreams.clusters)) {
const members = this.upstreams.clusters[clusterID].filter(
(member) => this.isReadOnlyAlertmanager(member) === false
);
if (members.length > 0) {
clusters[clusterID] = members;
}
}
return clusters;
},
getColorData(name: string, value: string): APILabelColorT | undefined {
if (this.colors[name] !== undefined) {
return this.colors[name][value];
}
},
setGrids(g: APIGridT[]) {
this.grids = g;
},
setUpstreams(u: APIAlertsResponseUpstreamsT) {
this.upstreams = u;
},
setInstances(i: APIAlertmanagerUpstreamT[]) {
this.upstreams.instances = i;
},
setClusters(c: APIAlertsResponseUpstreamsClusterMapT) {
this.upstreams.clusters = c;
},
},
{
gridPadding: computed,
readOnlyAlertmanagers: computed,
readWriteAlertmanagers: computed,
clustersWithoutReadOnly: computed,
setGrids: action.bound,
setUpstreams: action.bound,
setInstances: action.bound,
setClusters: action.bound,
},
{ name: "API Response data" }
);
info = observable(
{
authentication: {
enabled: false as boolean,
username: "",
},
totalAlerts: 0,
version: "unknown",
upgradeReady: false as boolean,
upgradeNeeded: false as boolean,
isRetrying: false as boolean,
reloadNeeded: false as boolean,
setIsRetrying() {
this.isRetrying = true;
},
clearIsRetrying() {
this.isRetrying = false;
},
setUpgradeNeeded() {
this.upgradeNeeded = true;
},
setReloadNeeded() {
this.reloadNeeded = true;
},
setTotalAlerts(n: number) {
this.totalAlerts = n;
},
},
{
setIsRetrying: action.bound,
clearIsRetrying: action.bound,
setReloadNeeded: action.bound,
setUpgradeNeeded: action.bound,
setTotalAlerts: action.bound,
},
{ name: "API response info" }
);
settings = observable(
{
values: {
staticColorLabels: [] as string[],
annotationsDefaultHidden: false as boolean,
annotationsHidden: [] as string[],
annotationsVisible: [] as string[],
sorting: {
grid: {
order: "startsAt",
reverse: false as boolean,
label: "alertname",
},
valueMapping: {},
},
silenceForm: {
strip: {
labels: [] as string[],
},
},
alertAcknowledgement: {
enabled: false as boolean,
durationSeconds: 900,
author: "karma / author missing",
commentPrefix: "",
},
},
},
{},
{
name: "Global settings",
}
);
status = observable(
{
value: AlertStoreStatuses.Idle,
lastUpdateAt: 0 as number | Date,
error: null as null | string,
stopped: false as boolean,
paused: false as boolean,
setIdle() {
this.value = AlertStoreStatuses.Idle;
this.error = null;
this.lastUpdateAt = new Date();
},
setFetching() {
this.value = AlertStoreStatuses.Fetching;
},
setProcessing() {
this.value = AlertStoreStatuses.Processing;
this.error = null;
},
setFailure(err: string) {
this.value = AlertStoreStatuses.Failure;
this.error = err;
this.lastUpdateAt = new Date();
},
pause() {
this.paused = true;
},
resume() {
this.paused = this.stopped ? true : false;
},
togglePause() {
this.paused = this.stopped ? true : !this.paused;
},
stop() {
this.paused = true;
this.stopped = true;
},
},
{
setIdle: action,
setFetching: action,
setProcessing: action,
setFailure: action,
pause: action.bound,
resume: action.bound,
togglePause: action.bound,
stop: action.bound,
},
{ name: "Store status" }
);
filters: AlertStoreFiltersT;
data: AlertStoreDataT;
info: AlertStoreInfoT;
settings: AlertStoreSettingsT;
status: AlertStoreStatusT;
constructor(initialFilters: null | string[]) {
this.filters = observable(
{
values: [] as FilterT[],
addFilter(raw: string) {
if (this.values.filter((f) => f.raw === raw).length === 0) {
this.values.push(NewUnappliedFilter(raw));
UpdateLocationSearch({ q: this.values.map((f) => f.raw) });
}
},
removeFilter(raw: string) {
if (this.values.filter((f) => f.raw === raw).length > 0) {
this.values = this.values.filter((f) => f.raw !== raw);
UpdateLocationSearch({ q: this.values.map((f) => f.raw) });
}
},
replaceFilter(oldRaw: string, newRaw: string) {
const index = this.values.findIndex((e) => e.raw === oldRaw);
if (index >= 0) {
// first check if we would create a duplicated filter
if (this.values.findIndex((e) => e.raw === newRaw) >= 0) {
// we already have newRaw, simply drop oldRaw
this.removeFilter(oldRaw);
} else {
// no dups, continue with a swap
this.values[index] = NewUnappliedFilter(newRaw);
UpdateLocationSearch({ q: this.values.map((f) => f.raw) });
}
}
},
setFilters(raws: string[]) {
this.values = raws.map((raw) => NewUnappliedFilter(raw));
UpdateLocationSearch({ q: this.values.map((f) => f.raw) });
},
setFilterValues(v: FilterT[]) {
this.values = v;
},
setWithoutLocation(raws: string[]) {
const filtersByRaw: { [key: string]: FilterT } = this.values.reduce(
function (map: { [key: string]: FilterT }, obj) {
map[toJS(obj.raw)] = toJS(obj);
return map;
},
{}
);
this.values = raws.map((raw) =>
filtersByRaw[raw] ? filtersByRaw[raw] : NewUnappliedFilter(raw)
);
},
applyAllFilters() {
for (let i = 0; i < this.values.length; i++) {
this.values[i].applied = true;
}
},
},
{
addFilter: action.bound,
removeFilter: action.bound,
replaceFilter: action.bound,
setFilters: action.bound,
setFilterValues: action.bound,
setWithoutLocation: action.bound,
applyAllFilters: action.bound,
},
{ name: "API Filters" }
);
this.data = observable(
{
colors: {} as APIAlertsResponseColorsT,
counters: [] as APILabelCounterT[],
grids: [] as APIGridT[],
silences: {} as APIAlertsResponseSilenceMapT,
upstreams: {
counters: { total: 0, healthy: 0, failed: 0 },
instances: [],
clusters: {},
} as APIAlertsResponseUpstreamsT,
receivers: [] as string[],
get gridPadding(): number {
return this.grids.filter((g) => g.labelName !== "").length > 0
? 5
: 0;
},
getAlertmanagerByName(
name: string
): APIAlertmanagerUpstreamT | undefined {
return this.upstreams.instances.find((am) => am.name === name);
},
isReadOnlyAlertmanager(name: string): boolean {
return this.readOnlyAlertmanagers.map((am) => am.name).includes(name);
},
getClusterAlertmanagersWithoutReadOnly(clusterID: string): string[] {
return this.clustersWithoutReadOnly[clusterID] || [];
},
get readOnlyAlertmanagers(): APIAlertmanagerUpstreamT[] {
return this.upstreams.instances.filter((am) => am.readonly === true);
},
get readWriteAlertmanagers(): APIAlertmanagerUpstreamT[] {
return this.upstreams.instances
.filter((am) => am.readonly === false)
.map((am) =>
Object.assign({}, am, {
clusterMembers: am.clusterMembers.filter(
(m) => this.isReadOnlyAlertmanager(m) === false
),
})
);
},
get clustersWithoutReadOnly(): APIAlertsResponseUpstreamsClusterMapT {
const clusters: APIAlertsResponseUpstreamsClusterMapT = {};
for (const clusterID of Object.keys(this.upstreams.clusters)) {
const members = this.upstreams.clusters[clusterID].filter(
(member) => this.isReadOnlyAlertmanager(member) === false
);
if (members.length > 0) {
clusters[clusterID] = members;
}
}
return clusters;
},
getColorData(name: string, value: string): APILabelColorT | undefined {
if (this.colors[name] !== undefined) {
return this.colors[name][value];
}
},
setGrids(g: APIGridT[]) {
this.grids = g;
},
setUpstreams(u: APIAlertsResponseUpstreamsT) {
this.upstreams = u;
},
setInstances(i: APIAlertmanagerUpstreamT[]) {
this.upstreams.instances = i;
},
setClusters(c: APIAlertsResponseUpstreamsClusterMapT) {
this.upstreams.clusters = c;
},
},
{
gridPadding: computed,
readOnlyAlertmanagers: computed,
readWriteAlertmanagers: computed,
clustersWithoutReadOnly: computed,
setGrids: action.bound,
setUpstreams: action.bound,
setInstances: action.bound,
setClusters: action.bound,
},
{ name: "API Response data" }
);
this.info = observable(
{
authentication: {
enabled: false as boolean,
username: "",
},
totalAlerts: 0,
version: "unknown",
upgradeReady: false as boolean,
upgradeNeeded: false as boolean,
isRetrying: false as boolean,
reloadNeeded: false as boolean,
setIsRetrying() {
this.isRetrying = true;
},
clearIsRetrying() {
this.isRetrying = false;
},
setUpgradeNeeded() {
this.upgradeNeeded = true;
},
setReloadNeeded() {
this.reloadNeeded = true;
},
setTotalAlerts(n: number) {
this.totalAlerts = n;
},
},
{
setIsRetrying: action.bound,
clearIsRetrying: action.bound,
setReloadNeeded: action.bound,
setUpgradeNeeded: action.bound,
setTotalAlerts: action.bound,
},
{ name: "API response info" }
);
this.settings = observable(
{
values: {
staticColorLabels: [] as string[],
annotationsDefaultHidden: false as boolean,
annotationsHidden: [] as string[],
annotationsVisible: [] as string[],
sorting: {
grid: {
order: "startsAt",
reverse: false as boolean,
label: "alertname",
},
valueMapping: {},
},
silenceForm: {
strip: {
labels: [] as string[],
},
},
alertAcknowledgement: {
enabled: false as boolean,
durationSeconds: 900,
author: "karma / author missing",
commentPrefix: "",
},
},
},
{},
{
name: "Global settings",
}
);
this.status = observable(
{
value: AlertStoreStatuses.Idle,
lastUpdateAt: 0 as number | Date,
error: null as null | string,
stopped: false as boolean,
paused: false as boolean,
setIdle() {
this.value = AlertStoreStatuses.Idle;
this.error = null;
this.lastUpdateAt = new Date();
},
setFetching() {
this.value = AlertStoreStatuses.Fetching;
},
setProcessing() {
this.value = AlertStoreStatuses.Processing;
this.error = null;
},
setFailure(err: string) {
this.value = AlertStoreStatuses.Failure;
this.error = err;
this.lastUpdateAt = new Date();
},
pause() {
this.paused = true;
},
resume() {
this.paused = this.stopped ? true : false;
},
togglePause() {
this.paused = this.stopped ? true : !this.paused;
},
stop() {
this.paused = true;
this.stopped = true;
},
},
{
setIdle: action,
setFetching: action,
setProcessing: action,
setFailure: action,
pause: action.bound,
resume: action.bound,
togglePause: action.bound,
stop: action.bound,
},
{ name: "Store status" }
);
if (initialFilters !== null) this.filters.setFilters(initialFilters);
}

View File

@@ -198,331 +198,408 @@ const UnpackRegexMatcherValues = (isRegex: boolean, value: string) => {
export type SilenceFormTabT = "editor" | "browser";
export type SilenceFormStageT = "form" | "preview" | "submit";
interface SilenceFormStoreToggleT {
visible: boolean;
blurred: boolean;
toggle: () => void;
hide: () => void;
show: () => void;
setBlur: (val: boolean) => void;
}
interface SilenceFormStoreTabT {
current: SilenceFormTabT;
setTab: (value: SilenceFormTabT) => void;
}
interface DurationT {
days: number;
hours: number;
minutes: number;
}
interface SilenceFormStoreDataT {
currentStage: SilenceFormStageT;
wasValidated: boolean;
silenceID: null | undefined | string;
alertmanagers: MultiValueOptionT[];
matchers: MatcherWithIDT[];
startsAt: Date;
endsAt: Date;
comment: string;
author: string;
requestsByCluster: { [key: string]: ClusterRequestT };
autofillMatchers: boolean;
resetInputs: boolean;
readonly toBase64: string;
fromBase64: (s: string) => void;
readonly isValid: boolean;
resetStartEnd: () => void;
resetProgress: () => void;
resetSilenceID: () => void;
setSilenceID: (id: string) => void;
setAlertmanagers: (val: MultiValueOptionT[]) => void;
setAutofillMatchers: (v: boolean) => void;
setResetInputs: (v: boolean) => void;
setStageSubmit: () => void;
setMatchers: (m: MatcherWithIDT[]) => void;
addEmptyMatcher: () => void;
deleteMatcher: (id: string) => void;
fillMatchersFromGroup: (
group: APIAlertGroupT,
stripLabels: string[],
alertmanagers: MultiValueOptionT[],
alerts?: APIAlertT[]
) => void;
fillFormFromSilence: (
alertmanager: APIAlertmanagerUpstreamT,
silence: AlertmanagerSilencePayloadT
) => void;
setAuthor: (a: string) => void;
setComment: (c: string) => void;
verifyStarEnd: () => void;
setStart: (startsAt: Date) => void;
setEnd: (endsAt: Date) => void;
incStart: (minutes: number) => void;
decStart: (minutes: number) => void;
incEnd: (minutes: number) => void;
decEnd: (minutes: number) => void;
setWasValidated: (v: boolean) => void;
readonly toAlertmanagerPayload: AlertmanagerSilencePayloadT;
readonly toDuration: DurationT;
}
class SilenceFormStore {
toggle = observable(
{
visible: false,
blurred: false,
toggle() {
this.visible = !this.visible;
},
hide() {
this.visible = false;
},
show() {
this.visible = true;
},
setBlur(val: boolean) {
this.blurred = val;
},
},
{
toggle: action.bound,
hide: action.bound,
show: action.bound,
setBlur: action.bound,
}
);
toggle: SilenceFormStoreToggleT;
tab: SilenceFormStoreTabT;
data: SilenceFormStoreDataT;
tab = observable(
{
current: "editor" as SilenceFormTabT,
setTab(value: SilenceFormTabT) {
this.current = value;
constructor() {
this.toggle = observable(
{
visible: false as boolean,
blurred: false as boolean,
toggle() {
this.visible = !this.visible;
},
hide() {
this.visible = false;
},
show() {
this.visible = true;
},
setBlur(val: boolean) {
this.blurred = val;
},
},
},
{
setTab: action.bound,
}
);
{
toggle: action.bound,
hide: action.bound,
show: action.bound,
setBlur: action.bound,
}
);
// form data is stored here, it's global (rather than attached to the form)
// so it can be manipulated from other parts of the code
// example: when user clicks a silence button on alert we should populate
// this form from that alert so user can easily silence that alert
data = observable(
{
currentStage: "form" as SilenceFormStageT,
wasValidated: false as boolean,
silenceID: null as null | undefined | string,
alertmanagers: [] as MultiValueOptionT[],
matchers: [] as MatcherWithIDT[],
startsAt: new Date(),
endsAt: addHours(new Date(), 1),
comment: "",
author: "",
requestsByCluster: {} as { [key: string]: ClusterRequestT },
autofillMatchers: true as boolean,
resetInputs: true as boolean,
get toBase64() {
const json = JSON.stringify({
am: this.alertmanagers,
m: this.matchers.map((m: MatcherWithIDT) => ({
n: m.name,
r: m.isRegex,
v: m.values.map((v) => v.value),
})),
d: differenceInMinutes(this.endsAt, this.startsAt),
c: this.comment,
});
return window.btoa(json);
this.tab = observable(
{
current: "editor" as SilenceFormTabT,
setTab(value: SilenceFormTabT) {
this.current = value;
},
},
{
setTab: action.bound,
}
);
// form data is stored here, it's global (rather than attached to the form)
// so it can be manipulated from other parts of the code
// example: when user clicks a silence button on alert we should populate
// this form from that alert so user can easily silence that alert
this.data = observable(
{
currentStage: "form" as SilenceFormStageT,
wasValidated: false as boolean,
silenceID: null as null | undefined | string,
alertmanagers: [] as MultiValueOptionT[],
matchers: [] as MatcherWithIDT[],
startsAt: new Date(),
endsAt: addHours(new Date(), 1),
comment: "",
author: "",
requestsByCluster: {} as { [key: string]: ClusterRequestT },
autofillMatchers: true as boolean,
resetInputs: true as boolean,
get toBase64() {
const json = JSON.stringify({
am: this.alertmanagers,
m: this.matchers.map((m: MatcherWithIDT) => ({
n: m.name,
r: m.isRegex,
v: m.values.map((v) => v.value),
})),
d: differenceInMinutes(this.endsAt, this.startsAt),
c: this.comment,
});
return window.btoa(json);
},
fromBase64(s: string) {
let parsed: SilenceFormDataFromBase64;
try {
parsed = JSON.parse(window.atob(s));
} catch (error) {
console.error(`Failed to parse JSON: ${error}`);
return false;
}
const matchers: MatcherWithIDT[] = [];
parsed.m.forEach((m: SimplifiedMatcherT) => {
const matcher = NewEmptyMatcher();
matcher.name = m.n;
matcher.isRegex = m.r;
matcher.values = m.v.map((v) => StringToOption(v));
matchers.push(matcher);
});
if (matchers.length > 0) {
this.alertmanagers = parsed.am;
this.matchers = matchers;
this.startsAt = new Date();
this.endsAt = addMinutes(this.startsAt, parsed.d);
this.comment = parsed.c;
this.silenceID = null;
this.autofillMatchers = false;
this.resetInputs = false;
return true;
}
fromBase64(s: string) {
let parsed: SilenceFormDataFromBase64;
try {
parsed = JSON.parse(window.atob(s));
} catch (error) {
console.error(`Failed to parse JSON: ${error}`);
return false;
}
},
const matchers: MatcherWithIDT[] = [];
parsed.m.forEach((m: SimplifiedMatcherT) => {
const matcher = NewEmptyMatcher();
matcher.name = m.n;
matcher.isRegex = m.r;
matcher.values = m.v.map((v) => StringToOption(v));
matchers.push(matcher);
});
get isValid() {
if (this.alertmanagers.length === 0) return false;
if (this.matchers.length === 0) return false;
if (
this.matchers.filter(
(m) =>
m.name === "" ||
m.values.length === 0 ||
m.values.filter((v) => v.value === "").length > 0
).length > 0
)
return false;
if (this.comment === "") return false;
if (this.author === "") return false;
return true;
},
if (matchers.length > 0) {
this.alertmanagers = parsed.am;
resetStartEnd() {
this.startsAt = new Date();
this.endsAt = addHours(new Date(), 1);
},
resetProgress() {
this.currentStage = "form";
this.wasValidated = false;
},
resetSilenceID() {
this.silenceID = null;
},
setSilenceID(id: string) {
this.silenceID = id;
},
setAlertmanagers(val: MultiValueOptionT[]) {
this.alertmanagers = val;
},
setAutofillMatchers(v: boolean) {
this.autofillMatchers = v;
},
setResetInputs(v: boolean) {
this.resetInputs = v;
},
setStageSubmit() {
this.currentStage = "submit";
},
setMatchers(m: MatcherWithIDT[]) {
this.matchers = m;
},
// append a new empty matcher to the list
addEmptyMatcher() {
this.matchers.push(NewEmptyMatcher());
},
deleteMatcher(id: string) {
// only delete matchers if we have more than 1
if (this.matchers.length > 1) {
this.matchers = this.matchers.filter((m) => m.id !== id);
}
},
// if alerts argument is not passed all group alerts will be used
fillMatchersFromGroup(
group: APIAlertGroupT,
stripLabels: string[],
alertmanagers: MultiValueOptionT[],
alerts?: APIAlertT[]
) {
this.alertmanagers = alertmanagers;
this.matchers = MatchersFromGroup(group, stripLabels, alerts);
// ensure that silenceID is nulled, since it's used to edit silences
// and this is used to silence groups
this.silenceID = null;
// disable matcher autofill
this.autofillMatchers = false;
// disable alertmanager input reset
this.resetInputs = false;
},
fillFormFromSilence(
alertmanager: APIAlertmanagerUpstreamT,
silence: AlertmanagerSilencePayloadT
) {
this.silenceID = silence.id;
this.alertmanagers = AlertmanagerClustersToOption({
[alertmanager.cluster]: alertmanager.clusterMembers,
});
const matchers: MatcherWithIDT[] = [];
for (const m of silence.matchers) {
const matcher = NewEmptyMatcher();
matcher.name = m.name;
matcher.values = UnpackRegexMatcherValues(m.isRegex, m.value);
matcher.isRegex = m.isRegex;
matchers.push(matcher);
}
this.matchers = matchers;
this.startsAt = new Date();
this.endsAt = addMinutes(this.startsAt, parsed.d);
this.comment = parsed.c;
this.startsAt = parseISO(silence.startsAt);
this.endsAt = parseISO(silence.endsAt);
this.comment = silence.comment;
this.author = silence.createdBy;
this.silenceID = null;
// disable matcher autofill
this.autofillMatchers = false;
this.resetInputs = false;
return true;
}
},
return false;
},
setAuthor(a: string) {
this.author = a;
},
get isValid() {
if (this.alertmanagers.length === 0) return false;
if (this.matchers.length === 0) return false;
if (
this.matchers.filter(
(m) =>
m.name === "" ||
m.values.length === 0 ||
m.values.filter((v) => v.value === "").length > 0
).length > 0
)
return false;
if (this.comment === "") return false;
if (this.author === "") return false;
return true;
},
setComment(c: string) {
this.comment = c;
},
resetStartEnd() {
this.startsAt = new Date();
this.endsAt = addHours(new Date(), 1);
},
verifyStarEnd() {
const now = new Date();
now.setSeconds(0);
if (this.startsAt < now) {
this.startsAt = now;
}
resetProgress() {
this.currentStage = "form";
this.wasValidated = false;
},
if (this.endsAt <= this.startsAt) {
this.endsAt = addMinutes(this.startsAt, 1);
}
},
setStart(startsAt: Date) {
this.startsAt = startsAt;
},
setEnd(endsAt: Date) {
this.endsAt = endsAt;
},
incStart(minutes: number) {
this.startsAt = addMinutes(this.startsAt, minutes);
this.verifyStarEnd();
},
decStart(minutes: number) {
this.startsAt = subMinutes(this.startsAt, minutes);
this.verifyStarEnd();
},
resetSilenceID() {
this.silenceID = null;
},
incEnd(minutes: number) {
this.endsAt = addMinutes(this.endsAt, minutes);
this.verifyStarEnd();
},
decEnd(minutes: number) {
this.endsAt = subMinutes(this.endsAt, minutes);
this.verifyStarEnd();
},
setSilenceID(id: string) {
this.silenceID = id;
},
setWasValidated(v: boolean) {
this.wasValidated = v;
},
setAlertmanagers(val: MultiValueOptionT[]) {
this.alertmanagers = val;
},
get toAlertmanagerPayload() {
const startsAt = new Date(this.startsAt);
startsAt.setSeconds(0);
startsAt.setMilliseconds(0);
const endsAt = new Date(this.endsAt);
endsAt.setSeconds(0);
endsAt.setMilliseconds(0);
return GenerateAlertmanagerSilenceData(
startsAt,
endsAt,
this.matchers,
this.author,
this.comment,
this.silenceID
);
},
setAutofillMatchers(v: boolean) {
this.autofillMatchers = v;
get toDuration() {
const data: DurationT = {
days: differenceInDays(this.endsAt, this.startsAt),
hours: differenceInHours(this.endsAt, this.startsAt) % 24,
minutes: differenceInMinutes(this.endsAt, this.startsAt) % 60,
};
return data;
},
},
setResetInputs(v: boolean) {
this.resetInputs = v;
{
toBase64: computed,
fromBase64: action.bound,
resetStartEnd: action.bound,
resetProgress: action.bound,
resetSilenceID: action.bound,
setSilenceID: action.bound,
setAlertmanagers: action.bound,
setAutofillMatchers: action.bound,
setResetInputs: action.bound,
setStageSubmit: action.bound,
setMatchers: action.bound,
addEmptyMatcher: action.bound,
deleteMatcher: action.bound,
fillMatchersFromGroup: action.bound,
fillFormFromSilence: action.bound,
setAuthor: action.bound,
setComment: action.bound,
verifyStarEnd: action.bound,
setStart: action.bound,
setEnd: action.bound,
incStart: action.bound,
decStart: action.bound,
incEnd: action.bound,
decEnd: action.bound,
isValid: computed,
setWasValidated: action.bound,
toAlertmanagerPayload: computed,
toDuration: computed,
},
setStageSubmit() {
this.currentStage = "submit";
},
setMatchers(m: MatcherWithIDT[]) {
this.matchers = m;
},
// append a new empty matcher to the list
addEmptyMatcher() {
this.matchers.push(NewEmptyMatcher());
},
deleteMatcher(id: string) {
// only delete matchers if we have more than 1
if (this.matchers.length > 1) {
this.matchers = this.matchers.filter((m) => m.id !== id);
}
},
// if alerts argument is not passed all group alerts will be used
fillMatchersFromGroup(
group: APIAlertGroupT,
stripLabels: string[],
alertmanagers: MultiValueOptionT[],
alerts?: APIAlertT[]
) {
this.alertmanagers = alertmanagers;
this.matchers = MatchersFromGroup(group, stripLabels, alerts);
// ensure that silenceID is nulled, since it's used to edit silences
// and this is used to silence groups
this.silenceID = null;
// disable matcher autofill
this.autofillMatchers = false;
// disable alertmanager input reset
this.resetInputs = false;
},
fillFormFromSilence(
alertmanager: APIAlertmanagerUpstreamT,
silence: AlertmanagerSilencePayloadT
) {
this.silenceID = silence.id;
this.alertmanagers = AlertmanagerClustersToOption({
[alertmanager.cluster]: alertmanager.clusterMembers,
});
const matchers: MatcherWithIDT[] = [];
for (const m of silence.matchers) {
const matcher = NewEmptyMatcher();
matcher.name = m.name;
matcher.values = UnpackRegexMatcherValues(m.isRegex, m.value);
matcher.isRegex = m.isRegex;
matchers.push(matcher);
}
this.matchers = matchers;
this.startsAt = parseISO(silence.startsAt);
this.endsAt = parseISO(silence.endsAt);
this.comment = silence.comment;
this.author = silence.createdBy;
// disable matcher autofill
this.autofillMatchers = false;
},
setAuthor(a: string) {
this.author = a;
},
setComment(c: string) {
this.comment = c;
},
verifyStarEnd() {
const now = new Date();
now.setSeconds(0);
if (this.startsAt < now) {
this.startsAt = now;
}
if (this.endsAt <= this.startsAt) {
this.endsAt = addMinutes(this.startsAt, 1);
}
},
setStart(startsAt: Date) {
this.startsAt = startsAt;
},
setEnd(endsAt: Date) {
this.endsAt = endsAt;
},
incStart(minutes: number) {
this.startsAt = addMinutes(this.startsAt, minutes);
this.verifyStarEnd();
},
decStart(minutes: number) {
this.startsAt = subMinutes(this.startsAt, minutes);
this.verifyStarEnd();
},
incEnd(minutes: number) {
this.endsAt = addMinutes(this.endsAt, minutes);
this.verifyStarEnd();
},
decEnd(minutes: number) {
this.endsAt = subMinutes(this.endsAt, minutes);
this.verifyStarEnd();
},
setWasValidated(v: boolean) {
this.wasValidated = v;
},
get toAlertmanagerPayload() {
const startsAt = new Date(this.startsAt);
startsAt.setSeconds(0);
startsAt.setMilliseconds(0);
const endsAt = new Date(this.endsAt);
endsAt.setSeconds(0);
endsAt.setMilliseconds(0);
return GenerateAlertmanagerSilenceData(
startsAt,
endsAt,
this.matchers,
this.author,
this.comment,
this.silenceID
);
},
get toDuration() {
const data = {
days: differenceInDays(this.endsAt, this.startsAt),
hours: differenceInHours(this.endsAt, this.startsAt) % 24,
minutes: differenceInMinutes(this.endsAt, this.startsAt) % 60,
};
return data;
},
},
{
toBase64: computed,
fromBase64: action.bound,
resetStartEnd: action.bound,
resetProgress: action.bound,
resetSilenceID: action.bound,
setSilenceID: action.bound,
setAlertmanagers: action.bound,
setAutofillMatchers: action.bound,
setResetInputs: action.bound,
setStageSubmit: action.bound,
setMatchers: action.bound,
addEmptyMatcher: action.bound,
deleteMatcher: action.bound,
fillMatchersFromGroup: action.bound,
fillFormFromSilence: action.bound,
setAuthor: action.bound,
setComment: action.bound,
verifyStarEnd: action.bound,
setStart: action.bound,
setEnd: action.bound,
incStart: action.bound,
decStart: action.bound,
incEnd: action.bound,
decEnd: action.bound,
isValid: computed,
setWasValidated: action.bound,
toAlertmanagerPayload: computed,
toDuration: computed,
},
{ name: "Silence form store" }
);
{ name: "Silence form store" }
);
}
}
export {