@oneuptime/common 10.0.70 → 10.0.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/Alert.ts +55 -0
- package/Models/DatabaseModels/Incident.ts +55 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +6 -4
- package/Models/DatabaseModels/Project.ts +5 -5
- package/Models/DatabaseModels/StatusPage.ts +80 -0
- package/Server/API/StatusPageAPI.ts +4 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.ts +6 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/AIBillingService.ts +2 -2
- package/Server/Services/AnalyticsDatabaseService.ts +17 -7
- package/Server/Services/BillingService.ts +116 -48
- package/Server/Services/NotificationService.ts +2 -2
- package/Server/Types/Database/QueryUtil.ts +13 -7
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +175 -29
- package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +71 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +170 -7
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +171 -2
- package/Server/Utils/Monitor/MonitorIncident.ts +212 -8
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +423 -1
- package/Server/Utils/Monitor/MonitorResource.ts +2 -0
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +99 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +268 -0
- package/Types/BaseDatabase/IncludesNone.ts +1 -4
- package/Types/Email.ts +50 -0
- package/Types/Infrastructure/BasicMetrics.ts +75 -0
- package/Types/Metrics/MetricQueryData.ts +11 -0
- package/Types/Monitor/CriteriaFilter.ts +10 -0
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +11 -0
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +10 -0
- package/Types/Monitor/MetricMonitor/MetricSeriesResult.ts +20 -0
- package/Types/Monitor/MonitorMetricType.ts +34 -0
- package/Types/Monitor/ServerMonitor/ServerMonitorResponse.ts +8 -0
- package/Types/Probe/ProbeApiIngestResponse.ts +25 -0
- package/Types/StatusPage/StatusPageLanguage.ts +29 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +17 -12
- package/UI/Components/Charts/Bar/BarChart.tsx +16 -11
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -0
- package/UI/Components/Charts/Line/LineChart.tsx +16 -11
- package/UI/Components/Filters/DateFilter.tsx +16 -8
- package/UI/Components/Filters/EntityFilter.tsx +33 -18
- package/UI/Components/Filters/FilterViewer.tsx +7 -5
- package/UI/Components/Filters/FiltersForm.tsx +27 -5
- package/UI/Components/Filters/NumberFilter.tsx +3 -2
- package/UI/Components/Filters/TextFilter.tsx +5 -4
- package/UI/Components/ModelTable/BaseModelTable.tsx +5 -3
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +453 -0
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +229 -0
- package/Utils/Metrics/MetricSeriesFingerprint.ts +97 -0
- package/Utils/Monitor/MonitorMetricType.ts +309 -19
- package/build/dist/Models/DatabaseModels/Alert.js +57 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +57 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +6 -4
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +5 -5
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +82 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +4 -0
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js +4 -2
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AIBillingService.js +2 -2
- package/build/dist/Server/Services/AIBillingService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +14 -4
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +99 -39
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/NotificationService.js +2 -2
- package/build/dist/Server/Services/NotificationService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryUtil.js +13 -7
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +132 -30
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +58 -7
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +134 -12
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +112 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +159 -15
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +373 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +2 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +65 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js +199 -0
- package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js.map +1 -1
- package/build/dist/Types/BaseDatabase/IncludesNone.js.map +1 -1
- package/build/dist/Types/Email.js +42 -0
- package/build/dist/Types/Email.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +10 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/MetricMonitor/MetricSeriesResult.js +2 -0
- package/build/dist/Types/Monitor/MetricMonitor/MetricSeriesResult.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorMetricType.js +28 -0
- package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Types/StatusPage/StatusPageLanguage.js +21 -0
- package/build/dist/Types/StatusPage/StatusPageLanguage.js.map +1 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +13 -12
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +12 -11
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +11 -3
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +12 -11
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Filters/DateFilter.js +1 -4
- package/build/dist/UI/Components/Filters/DateFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/EntityFilter.js +21 -14
- package/build/dist/UI/Components/Filters/EntityFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/FilterViewer.js +1 -2
- package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
- package/build/dist/UI/Components/Filters/FiltersForm.js +7 -3
- package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
- package/build/dist/UI/Components/Filters/NumberFilter.js +0 -1
- package/build/dist/UI/Components/Filters/NumberFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/TextFilter.js +5 -4
- package/build/dist/UI/Components/Filters/TextFilter.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +5 -3
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +383 -0
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -0
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +109 -0
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricSeriesFingerprint.js +81 -0
- package/build/dist/Utils/Metrics/MetricSeriesFingerprint.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorMetricType.js +287 -19
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import MetricMonitorCriteria from "../../../../../Server/Utils/Monitor/Criteria/MetricMonitorCriteria";
|
|
2
|
+
import MetricSeriesFingerprint from "../../../../../Utils/Metrics/MetricSeriesFingerprint";
|
|
2
3
|
import AggregateModel from "../../../../../Types/BaseDatabase/AggregatedModel";
|
|
3
4
|
import AggregatedResult from "../../../../../Types/BaseDatabase/AggregatedResult";
|
|
4
5
|
import {
|
|
@@ -500,3 +501,270 @@ describe("MetricMonitorCriteria.isMonitorInstanceCriteriaFilterMet", () => {
|
|
|
500
501
|
expect(message).not.toContain("110, 120, 130, 140, 150, 160");
|
|
501
502
|
});
|
|
502
503
|
});
|
|
504
|
+
|
|
505
|
+
describe("MetricMonitorCriteria.evaluateAllSeries — per-host alerting", () => {
|
|
506
|
+
/*
|
|
507
|
+
* Build a fixture whose metric response carries a pre-computed
|
|
508
|
+
* seriesBreakdown with two hosts over threshold and one under. This
|
|
509
|
+
* mirrors what MonitorTelemetryMonitor produces when groupByAttributeKeys
|
|
510
|
+
* is set.
|
|
511
|
+
*/
|
|
512
|
+
function buildInputsWithSeriesBreakdown(input: {
|
|
513
|
+
criteriaFilter: CriteriaFilter;
|
|
514
|
+
seriesSamples: Array<{
|
|
515
|
+
labels: Record<string, string>;
|
|
516
|
+
values: Array<number>;
|
|
517
|
+
}>;
|
|
518
|
+
}): {
|
|
519
|
+
criteriaFilter: CriteriaFilter;
|
|
520
|
+
monitorStep: MonitorStep;
|
|
521
|
+
dataToProcess: MetricMonitorResponse;
|
|
522
|
+
} {
|
|
523
|
+
const aliasData: MetricAliasData = {
|
|
524
|
+
metricVariable: "a",
|
|
525
|
+
title: "CPU",
|
|
526
|
+
description: undefined,
|
|
527
|
+
legend: undefined,
|
|
528
|
+
legendUnit: "%",
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const queryConfig: MetricQueryConfigData = {
|
|
532
|
+
metricAliasData: aliasData,
|
|
533
|
+
metricQueryData: {
|
|
534
|
+
filterData: {
|
|
535
|
+
metricName: "cpu.usage",
|
|
536
|
+
},
|
|
537
|
+
groupByAttributeKeys: ["host.name"],
|
|
538
|
+
} as unknown as MetricQueryData,
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const metricViewConfig: MetricsViewConfig = {
|
|
542
|
+
queryConfigs: [queryConfig],
|
|
543
|
+
formulaConfigs: [],
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
const monitorStep: MonitorStep = new MonitorStep();
|
|
547
|
+
monitorStep.data = {
|
|
548
|
+
id: ObjectID.generate().toString(),
|
|
549
|
+
monitorCriteria: { data: undefined } as never,
|
|
550
|
+
} as unknown as MonitorStep["data"];
|
|
551
|
+
monitorStep.data!.metricMonitor = {
|
|
552
|
+
metricViewConfig,
|
|
553
|
+
rollingTime: RollingTime.Past1Minute,
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
const seriesBreakdown: Array<{
|
|
557
|
+
fingerprint: string;
|
|
558
|
+
labels: Record<string, string>;
|
|
559
|
+
aggregatedResults: Array<AggregatedResult>;
|
|
560
|
+
}> = input.seriesSamples.map(
|
|
561
|
+
(s: { labels: Record<string, string>; values: Array<number> }) => {
|
|
562
|
+
return {
|
|
563
|
+
fingerprint: Object.values(s.labels).join("|"),
|
|
564
|
+
labels: s.labels,
|
|
565
|
+
aggregatedResults: [
|
|
566
|
+
{
|
|
567
|
+
data: s.values.map((v: number) => {
|
|
568
|
+
return {
|
|
569
|
+
timestamp: new Date(),
|
|
570
|
+
value: v,
|
|
571
|
+
attributes: s.labels,
|
|
572
|
+
} as unknown as AggregateModel;
|
|
573
|
+
}),
|
|
574
|
+
} as AggregatedResult,
|
|
575
|
+
],
|
|
576
|
+
};
|
|
577
|
+
},
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
// Flat metricResult = union of all series samples, mirroring the worker.
|
|
581
|
+
const flatData: Array<AggregateModel> = input.seriesSamples.flatMap(
|
|
582
|
+
(s: { labels: Record<string, string>; values: Array<number> }) => {
|
|
583
|
+
return s.values.map((v: number) => {
|
|
584
|
+
return {
|
|
585
|
+
timestamp: new Date(),
|
|
586
|
+
value: v,
|
|
587
|
+
attributes: s.labels,
|
|
588
|
+
} as unknown as AggregateModel;
|
|
589
|
+
});
|
|
590
|
+
},
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
const dataToProcess: MetricMonitorResponse = {
|
|
594
|
+
projectId: ObjectID.generate(),
|
|
595
|
+
metricResult: [{ data: flatData } as AggregatedResult],
|
|
596
|
+
metricViewConfig,
|
|
597
|
+
monitorId: ObjectID.generate(),
|
|
598
|
+
seriesBreakdown:
|
|
599
|
+
seriesBreakdown as unknown as MetricMonitorResponse["seriesBreakdown"],
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
return {
|
|
603
|
+
criteriaFilter: input.criteriaFilter,
|
|
604
|
+
monitorStep,
|
|
605
|
+
dataToProcess,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
test("no seriesBreakdown → single synthetic evaluation (backward compatible)", () => {
|
|
610
|
+
const criteriaFilter: CriteriaFilter = {
|
|
611
|
+
checkOn: CheckOn.MetricValue,
|
|
612
|
+
filterType: FilterType.GreaterThan,
|
|
613
|
+
value: "80",
|
|
614
|
+
metricMonitorOptions: {
|
|
615
|
+
metricAlias: "a",
|
|
616
|
+
metricAggregationType: EvaluateOverTimeType.AnyValue,
|
|
617
|
+
},
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
const inputs: ReturnType<typeof buildInputs> = buildInputs({
|
|
621
|
+
metricNativeUnit: "%",
|
|
622
|
+
sampleValues: [95],
|
|
623
|
+
criteriaFilter,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
const results: ReturnType<typeof MetricMonitorCriteria.evaluateAllSeries> =
|
|
627
|
+
MetricMonitorCriteria.evaluateAllSeries(inputs);
|
|
628
|
+
expect(results).toHaveLength(1);
|
|
629
|
+
expect(results[0]!.fingerprint).toBeUndefined();
|
|
630
|
+
expect(results[0]!.rootCause).toBeTruthy();
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
test("seriesBreakdown with 2 breaching + 1 non-breaching → only breaching series return rootCause", () => {
|
|
634
|
+
const criteriaFilter: CriteriaFilter = {
|
|
635
|
+
checkOn: CheckOn.MetricValue,
|
|
636
|
+
filterType: FilterType.GreaterThan,
|
|
637
|
+
value: "80",
|
|
638
|
+
metricMonitorOptions: {
|
|
639
|
+
metricAlias: "a",
|
|
640
|
+
metricAggregationType: EvaluateOverTimeType.AnyValue,
|
|
641
|
+
},
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
const inputs: ReturnType<typeof buildInputsWithSeriesBreakdown> =
|
|
645
|
+
buildInputsWithSeriesBreakdown({
|
|
646
|
+
criteriaFilter,
|
|
647
|
+
seriesSamples: [
|
|
648
|
+
{ labels: { "host.name": "prod-01" }, values: [95] },
|
|
649
|
+
{ labels: { "host.name": "prod-02" }, values: [92] },
|
|
650
|
+
{ labels: { "host.name": "prod-03" }, values: [50] },
|
|
651
|
+
],
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
const results: ReturnType<typeof MetricMonitorCriteria.evaluateAllSeries> =
|
|
655
|
+
MetricMonitorCriteria.evaluateAllSeries(inputs);
|
|
656
|
+
expect(results).toHaveLength(3);
|
|
657
|
+
|
|
658
|
+
const breaching: typeof results = results.filter(
|
|
659
|
+
(r: (typeof results)[number]) => {
|
|
660
|
+
return r.rootCause !== null;
|
|
661
|
+
},
|
|
662
|
+
);
|
|
663
|
+
expect(breaching).toHaveLength(2);
|
|
664
|
+
|
|
665
|
+
const breachingHosts: Array<string> = breaching
|
|
666
|
+
.map((r: (typeof results)[number]) => {
|
|
667
|
+
return r.labels["host.name"] as string;
|
|
668
|
+
})
|
|
669
|
+
.sort();
|
|
670
|
+
expect(breachingHosts).toEqual(["prod-01", "prod-02"]);
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
test("each series gets its own breaching-samples context", () => {
|
|
674
|
+
const criteriaFilter: CriteriaFilter = {
|
|
675
|
+
checkOn: CheckOn.MetricValue,
|
|
676
|
+
filterType: FilterType.GreaterThan,
|
|
677
|
+
value: "80",
|
|
678
|
+
metricMonitorOptions: {
|
|
679
|
+
metricAlias: "a",
|
|
680
|
+
metricAggregationType: EvaluateOverTimeType.AnyValue,
|
|
681
|
+
},
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
const inputs: ReturnType<typeof buildInputsWithSeriesBreakdown> =
|
|
685
|
+
buildInputsWithSeriesBreakdown({
|
|
686
|
+
criteriaFilter,
|
|
687
|
+
seriesSamples: [
|
|
688
|
+
{ labels: { "host.name": "prod-01" }, values: [95, 97] },
|
|
689
|
+
{ labels: { "host.name": "prod-02" }, values: [92] },
|
|
690
|
+
],
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
const results: ReturnType<typeof MetricMonitorCriteria.evaluateAllSeries> =
|
|
694
|
+
MetricMonitorCriteria.evaluateAllSeries(inputs);
|
|
695
|
+
const prod01: (typeof results)[number] = results.find(
|
|
696
|
+
(r: (typeof results)[number]) => {
|
|
697
|
+
return r.labels["host.name"] === "prod-01";
|
|
698
|
+
},
|
|
699
|
+
)!;
|
|
700
|
+
const prod02: (typeof results)[number] = results.find(
|
|
701
|
+
(r: (typeof results)[number]) => {
|
|
702
|
+
return r.labels["host.name"] === "prod-02";
|
|
703
|
+
},
|
|
704
|
+
)!;
|
|
705
|
+
|
|
706
|
+
expect(prod01.context.breachingSamples).toHaveLength(2);
|
|
707
|
+
expect(prod02.context.breachingSamples).toHaveLength(1);
|
|
708
|
+
expect(prod01.context.seriesLabels).toEqual({ "host.name": "prod-01" });
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
describe("MetricSeriesFingerprint", () => {
|
|
713
|
+
test("fingerprint is stable regardless of key order", () => {
|
|
714
|
+
const a: { [k: string]: string } = {
|
|
715
|
+
"host.name": "prod-01",
|
|
716
|
+
region: "us-east",
|
|
717
|
+
};
|
|
718
|
+
const b: { [k: string]: string } = {
|
|
719
|
+
region: "us-east",
|
|
720
|
+
"host.name": "prod-01",
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
expect(MetricSeriesFingerprint.computeFingerprint(a)).toBe(
|
|
724
|
+
MetricSeriesFingerprint.computeFingerprint(b),
|
|
725
|
+
);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
test("different label values → different fingerprints", () => {
|
|
729
|
+
expect(
|
|
730
|
+
MetricSeriesFingerprint.computeFingerprint({ "host.name": "prod-01" }),
|
|
731
|
+
).not.toBe(
|
|
732
|
+
MetricSeriesFingerprint.computeFingerprint({ "host.name": "prod-02" }),
|
|
733
|
+
);
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
test("empty labels → sentinel WholeMonitorFingerprint", () => {
|
|
737
|
+
expect(MetricSeriesFingerprint.computeFingerprint({})).toBe(
|
|
738
|
+
MetricSeriesFingerprint.WholeMonitorFingerprint,
|
|
739
|
+
);
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
test("missing attribute keys canonicalize to empty string (stable fingerprint)", () => {
|
|
743
|
+
const sample1: AggregateModel = {
|
|
744
|
+
timestamp: new Date(),
|
|
745
|
+
value: 42,
|
|
746
|
+
attributes: { "host.name": "prod-01" },
|
|
747
|
+
} as unknown as AggregateModel;
|
|
748
|
+
const sample2: AggregateModel = {
|
|
749
|
+
timestamp: new Date(),
|
|
750
|
+
value: 42,
|
|
751
|
+
attributes: { "host.name": "prod-01", region: "us-east" },
|
|
752
|
+
} as unknown as AggregateModel;
|
|
753
|
+
|
|
754
|
+
const labels1: import("../../../../../Types/JSON").JSONObject =
|
|
755
|
+
MetricSeriesFingerprint.extractSeriesLabels({
|
|
756
|
+
sample: sample1,
|
|
757
|
+
attributeKeys: ["host.name"],
|
|
758
|
+
});
|
|
759
|
+
const labels2: import("../../../../../Types/JSON").JSONObject =
|
|
760
|
+
MetricSeriesFingerprint.extractSeriesLabels({
|
|
761
|
+
sample: sample2,
|
|
762
|
+
attributeKeys: ["host.name"],
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
// When only host.name is selected, region doesn't affect the fingerprint
|
|
766
|
+
expect(MetricSeriesFingerprint.computeFingerprint(labels1)).toBe(
|
|
767
|
+
MetricSeriesFingerprint.computeFingerprint(labels2),
|
|
768
|
+
);
|
|
769
|
+
});
|
|
770
|
+
});
|
|
@@ -4,10 +4,7 @@ import JSONFunctions from "../JSONFunctions";
|
|
|
4
4
|
import ObjectID from "../ObjectID";
|
|
5
5
|
import QueryOperator from "./QueryOperator";
|
|
6
6
|
|
|
7
|
-
export type IncludesNoneType =
|
|
8
|
-
| Array<string>
|
|
9
|
-
| Array<ObjectID>
|
|
10
|
-
| Array<number>;
|
|
7
|
+
export type IncludesNoneType = Array<string> | Array<ObjectID> | Array<number>;
|
|
11
8
|
|
|
12
9
|
export default class IncludesNone extends QueryOperator<IncludesNoneType> {
|
|
13
10
|
private _values: IncludesNoneType = [];
|
package/Types/Email.ts
CHANGED
|
@@ -75,6 +75,56 @@ export default class Email extends DatabaseProperty {
|
|
|
75
75
|
return new Email(value);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
public static parseList(value: string | null | undefined): Array<Email> {
|
|
79
|
+
if (!value) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const seen: Set<string> = new Set<string>();
|
|
84
|
+
const emails: Array<Email> = [];
|
|
85
|
+
|
|
86
|
+
for (const part of value.split(/[,;\s]+/)) {
|
|
87
|
+
const trimmed: string = part.trim();
|
|
88
|
+
if (!trimmed) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const email: Email = new Email(trimmed);
|
|
92
|
+
const key: string = email.toString();
|
|
93
|
+
if (!seen.has(key)) {
|
|
94
|
+
seen.add(key);
|
|
95
|
+
emails.push(email);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return emails;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public static isValidList(value: string | null | undefined): boolean {
|
|
103
|
+
if (!value) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const parts: Array<string> = value
|
|
108
|
+
.split(/[,;\s]+/)
|
|
109
|
+
.map((p: string): string => {
|
|
110
|
+
return p.trim();
|
|
111
|
+
})
|
|
112
|
+
.filter((p: string): boolean => {
|
|
113
|
+
return p.length > 0;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (parts.length === 0) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
for (const part of parts) {
|
|
121
|
+
if (!Email.isValid(part)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
|
|
78
128
|
public static override fromJSON(json: JSONObject): Email {
|
|
79
129
|
if (json["_type"] === ObjectType.Email) {
|
|
80
130
|
return new Email((json["value"] as string) || "");
|
|
@@ -4,11 +4,30 @@ export interface MemoryMetrics {
|
|
|
4
4
|
used: number;
|
|
5
5
|
percentUsed: number;
|
|
6
6
|
percentFree: number;
|
|
7
|
+
|
|
8
|
+
available?: number | undefined;
|
|
9
|
+
buffers?: number | undefined;
|
|
10
|
+
cached?: number | undefined;
|
|
11
|
+
|
|
12
|
+
swapTotal?: number | undefined;
|
|
13
|
+
swapUsed?: number | undefined;
|
|
14
|
+
swapFree?: number | undefined;
|
|
15
|
+
swapPercentUsed?: number | undefined;
|
|
7
16
|
}
|
|
8
17
|
|
|
9
18
|
export interface CPUMetrics {
|
|
10
19
|
percentUsed: number;
|
|
11
20
|
cores: number;
|
|
21
|
+
|
|
22
|
+
perCorePercent?: Array<number> | undefined;
|
|
23
|
+
timeUserPercent?: number | undefined;
|
|
24
|
+
timeSystemPercent?: number | undefined;
|
|
25
|
+
timeIdlePercent?: number | undefined;
|
|
26
|
+
timeIoWaitPercent?: number | undefined;
|
|
27
|
+
timeStealPercent?: number | undefined;
|
|
28
|
+
timeNicePercent?: number | undefined;
|
|
29
|
+
timeIrqPercent?: number | undefined;
|
|
30
|
+
timeSoftIrqPercent?: number | undefined;
|
|
12
31
|
}
|
|
13
32
|
|
|
14
33
|
export interface BasicDiskMetrics {
|
|
@@ -18,10 +37,66 @@ export interface BasicDiskMetrics {
|
|
|
18
37
|
diskPath: string;
|
|
19
38
|
percentUsed: number;
|
|
20
39
|
percentFree: number;
|
|
40
|
+
|
|
41
|
+
device?: string | undefined;
|
|
42
|
+
fstype?: string | undefined;
|
|
43
|
+
readBytes?: number | undefined;
|
|
44
|
+
writeBytes?: number | undefined;
|
|
45
|
+
readCount?: number | undefined;
|
|
46
|
+
writeCount?: number | undefined;
|
|
47
|
+
ioTimeMs?: number | undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface NetworkInterfaceMetrics {
|
|
51
|
+
interfaceName: string;
|
|
52
|
+
bytesReceived: number;
|
|
53
|
+
bytesSent: number;
|
|
54
|
+
packetsReceived: number;
|
|
55
|
+
packetsSent: number;
|
|
56
|
+
errorsIn: number;
|
|
57
|
+
errorsOut: number;
|
|
58
|
+
dropsIn: number;
|
|
59
|
+
dropsOut: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface NetworkMetrics {
|
|
63
|
+
interfaces?: Array<NetworkInterfaceMetrics> | undefined;
|
|
64
|
+
totalBytesReceived?: number | undefined;
|
|
65
|
+
totalBytesSent?: number | undefined;
|
|
66
|
+
totalPacketsReceived?: number | undefined;
|
|
67
|
+
totalPacketsSent?: number | undefined;
|
|
68
|
+
connectionsEstablished?: number | undefined;
|
|
69
|
+
connectionsListen?: number | undefined;
|
|
70
|
+
connectionsTotal?: number | undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface LoadMetrics {
|
|
74
|
+
load1: number;
|
|
75
|
+
load5: number;
|
|
76
|
+
load15: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface HostMetrics {
|
|
80
|
+
platform?: string | undefined;
|
|
81
|
+
platformFamily?: string | undefined;
|
|
82
|
+
platformVersion?: string | undefined;
|
|
83
|
+
kernelVersion?: string | undefined;
|
|
84
|
+
kernelArch?: string | undefined;
|
|
85
|
+
os?: string | undefined;
|
|
86
|
+
uptimeSeconds?: number | undefined;
|
|
87
|
+
bootTime?: number | undefined;
|
|
88
|
+
hostId?: string | undefined;
|
|
89
|
+
virtualizationSystem?: string | undefined;
|
|
90
|
+
virtualizationRole?: string | undefined;
|
|
91
|
+
numProcesses?: number | undefined;
|
|
21
92
|
}
|
|
22
93
|
|
|
23
94
|
export default interface BasicInfrastructureMetrics {
|
|
24
95
|
cpuMetrics: CPUMetrics;
|
|
25
96
|
memoryMetrics: MemoryMetrics;
|
|
26
97
|
diskMetrics: Array<BasicDiskMetrics>;
|
|
98
|
+
|
|
99
|
+
networkMetrics?: NetworkMetrics | undefined;
|
|
100
|
+
loadMetrics?: LoadMetrics | undefined;
|
|
101
|
+
hostMetrics?: HostMetrics | undefined;
|
|
27
102
|
}
|
|
@@ -6,4 +6,15 @@ import MetricsQuery from "./MetricsQuery";
|
|
|
6
6
|
export default interface MetricQueryData {
|
|
7
7
|
filterData: FilterData<MetricsQuery>;
|
|
8
8
|
groupBy?: GroupBy<Metric> | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* OpenTelemetry attribute keys (e.g. "host.name", "service.name") to
|
|
11
|
+
* group this query by. Stored alongside groupBy because attributes
|
|
12
|
+
* live inside a nested Map column and can't be expressed through
|
|
13
|
+
* GroupBy<Metric>, which only references top-level columns.
|
|
14
|
+
*
|
|
15
|
+
* When set, the monitor worker emits one series per unique value
|
|
16
|
+
* combination of these keys — enabling per-host (or per-service, per-
|
|
17
|
+
* whatever) incident creation from a single metric monitor.
|
|
18
|
+
*/
|
|
19
|
+
groupByAttributeKeys?: Array<string> | undefined;
|
|
9
20
|
}
|
|
@@ -19,6 +19,11 @@ export enum CheckOn {
|
|
|
19
19
|
DiskUsagePercent = "Disk Usage (in %)",
|
|
20
20
|
CPUUsagePercent = "CPU Usage (in %)",
|
|
21
21
|
MemoryUsagePercent = "Memory Usage (in %)",
|
|
22
|
+
LoadAverage1Min = "Load Average (1 minute)",
|
|
23
|
+
LoadAverage5Min = "Load Average (5 minute)",
|
|
24
|
+
LoadAverage15Min = "Load Average (15 minute)",
|
|
25
|
+
SwapUsagePercent = "Swap Usage (in %)",
|
|
26
|
+
CPUIoWaitPercent = "CPU IO Wait (in %)",
|
|
22
27
|
ExpiresInHours = "Expires In Hours",
|
|
23
28
|
ExpiresInDays = "Expires In Days",
|
|
24
29
|
IsSelfSignedCertificate = "Is Self Signed Certificate",
|
|
@@ -296,6 +301,11 @@ export class CriteriaFilterUtil {
|
|
|
296
301
|
checkOn === CheckOn.DiskUsagePercent ||
|
|
297
302
|
checkOn === CheckOn.CPUUsagePercent ||
|
|
298
303
|
checkOn === CheckOn.MemoryUsagePercent ||
|
|
304
|
+
checkOn === CheckOn.LoadAverage1Min ||
|
|
305
|
+
checkOn === CheckOn.LoadAverage5Min ||
|
|
306
|
+
checkOn === CheckOn.LoadAverage15Min ||
|
|
307
|
+
checkOn === CheckOn.SwapUsagePercent ||
|
|
308
|
+
checkOn === CheckOn.CPUIoWaitPercent ||
|
|
299
309
|
checkOn === CheckOn.IsOnline ||
|
|
300
310
|
checkOn === CheckOn.SnmpResponseTime ||
|
|
301
311
|
checkOn === CheckOn.SnmpIsOnline ||
|
|
@@ -47,6 +47,17 @@ export default interface MetricCriteriaContext {
|
|
|
47
47
|
filterAttributes: JSONObject;
|
|
48
48
|
groupBy: Array<string>;
|
|
49
49
|
timeWindowMinutes?: number | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Fingerprint of the specific series this context represents when the
|
|
52
|
+
* monitor is configured for per-series alerting. Undefined for
|
|
53
|
+
* traditional whole-monitor evaluation.
|
|
54
|
+
*/
|
|
55
|
+
seriesFingerprint?: string | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Label values identifying the series (e.g. {host.name: prod-01}).
|
|
58
|
+
* Populated alongside seriesFingerprint.
|
|
59
|
+
*/
|
|
60
|
+
seriesLabels?: JSONObject | undefined;
|
|
50
61
|
breachingSample?: MetricBreachingSample | undefined;
|
|
51
62
|
/**
|
|
52
63
|
* All samples in the evaluation window that breached the threshold,
|
|
@@ -4,6 +4,7 @@ import MonitorEvaluationSummary from "../MonitorEvaluationSummary";
|
|
|
4
4
|
import MetricsViewConfig from "../../Metrics/MetricsViewConfig";
|
|
5
5
|
import ObjectID from "../../ObjectID";
|
|
6
6
|
import Dictionary from "../../Dictionary";
|
|
7
|
+
import MetricSeriesResult from "./MetricSeriesResult";
|
|
7
8
|
|
|
8
9
|
export interface KubernetesAffectedResource {
|
|
9
10
|
podName?: string | undefined;
|
|
@@ -31,4 +32,13 @@ export default interface MetricMonitorResponse {
|
|
|
31
32
|
monitorId: ObjectID;
|
|
32
33
|
evaluationSummary?: MonitorEvaluationSummary | undefined;
|
|
33
34
|
kubernetesResourceBreakdown?: KubernetesResourceBreakdown | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Per-series breakdown when any queryConfig sets groupByAttributeKeys.
|
|
37
|
+
* Each entry carries a fingerprint, the label values identifying that
|
|
38
|
+
* series, and the aggregated-per-query results scoped to that series
|
|
39
|
+
* (including per-series formula results). Absent for traditional
|
|
40
|
+
* whole-monitor evaluation, in which case criteria evaluate against
|
|
41
|
+
* `metricResult` as before.
|
|
42
|
+
*/
|
|
43
|
+
seriesBreakdown?: Array<MetricSeriesResult> | undefined;
|
|
34
44
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import AggregatedResult from "../../BaseDatabase/AggregatedResult";
|
|
2
|
+
import { JSONObject } from "../../JSON";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* One series within a metric monitor evaluation. When a metric monitor
|
|
6
|
+
* is configured with group-by attributes (e.g. host.name), the worker
|
|
7
|
+
* splits the aggregated results into one entry per unique label
|
|
8
|
+
* combination. Each series is then evaluated independently so the
|
|
9
|
+
* criteria can fire one incident per affected series.
|
|
10
|
+
*
|
|
11
|
+
* `aggregatedResults` is aligned with the monitor's queryConfigs +
|
|
12
|
+
* formulaConfigs arrays (same length, same order) so per-series
|
|
13
|
+
* formula evaluation reuses the same indexing the criteria evaluator
|
|
14
|
+
* expects.
|
|
15
|
+
*/
|
|
16
|
+
export default interface MetricSeriesResult {
|
|
17
|
+
fingerprint: string;
|
|
18
|
+
labels: JSONObject;
|
|
19
|
+
aggregatedResults: Array<AggregatedResult>;
|
|
20
|
+
}
|
|
@@ -6,6 +6,40 @@ enum MonitorMetricType {
|
|
|
6
6
|
MemoryUsagePercent = "oneuptime.monitor.memory.usage.percent",
|
|
7
7
|
IsOnline = "oneuptime.monitor.online",
|
|
8
8
|
ExecutionTime = "oneuptime.monitor.execution.time",
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Extended server/VM metrics. Emitted when the agent payload contains them;
|
|
12
|
+
* absent for older agents, which keeps the pipeline backwards-compatible.
|
|
13
|
+
*/
|
|
14
|
+
LoadAverage1Min = "oneuptime.monitor.load.avg.1min",
|
|
15
|
+
LoadAverage5Min = "oneuptime.monitor.load.avg.5min",
|
|
16
|
+
LoadAverage15Min = "oneuptime.monitor.load.avg.15min",
|
|
17
|
+
|
|
18
|
+
SwapUsagePercent = "oneuptime.monitor.memory.swap.usage.percent",
|
|
19
|
+
MemoryAvailableBytes = "oneuptime.monitor.memory.available.bytes",
|
|
20
|
+
|
|
21
|
+
CPUTimeUserPercent = "oneuptime.monitor.cpu.time.user.percent",
|
|
22
|
+
CPUTimeSystemPercent = "oneuptime.monitor.cpu.time.system.percent",
|
|
23
|
+
CPUTimeIoWaitPercent = "oneuptime.monitor.cpu.time.iowait.percent",
|
|
24
|
+
CPUTimeIdlePercent = "oneuptime.monitor.cpu.time.idle.percent",
|
|
25
|
+
CPUTimeStealPercent = "oneuptime.monitor.cpu.time.steal.percent",
|
|
26
|
+
|
|
27
|
+
DiskReadBytesTotal = "oneuptime.monitor.disk.io.read.bytes.total",
|
|
28
|
+
DiskWriteBytesTotal = "oneuptime.monitor.disk.io.write.bytes.total",
|
|
29
|
+
DiskReadOpsTotal = "oneuptime.monitor.disk.io.read.ops.total",
|
|
30
|
+
DiskWriteOpsTotal = "oneuptime.monitor.disk.io.write.ops.total",
|
|
31
|
+
|
|
32
|
+
NetworkBytesReceivedTotal = "oneuptime.monitor.network.bytes.received.total",
|
|
33
|
+
NetworkBytesSentTotal = "oneuptime.monitor.network.bytes.sent.total",
|
|
34
|
+
NetworkPacketsReceivedTotal = "oneuptime.monitor.network.packets.received.total",
|
|
35
|
+
NetworkPacketsSentTotal = "oneuptime.monitor.network.packets.sent.total",
|
|
36
|
+
NetworkErrorsIn = "oneuptime.monitor.network.errors.in",
|
|
37
|
+
NetworkErrorsOut = "oneuptime.monitor.network.errors.out",
|
|
38
|
+
NetworkConnectionsEstablished = "oneuptime.monitor.network.connections.established",
|
|
39
|
+
NetworkConnectionsListen = "oneuptime.monitor.network.connections.listen",
|
|
40
|
+
|
|
41
|
+
HostUptimeSeconds = "oneuptime.monitor.host.uptime.seconds",
|
|
42
|
+
ProcessCountTotal = "oneuptime.monitor.process.count.total",
|
|
9
43
|
}
|
|
10
44
|
|
|
11
45
|
export default MonitorMetricType;
|
|
@@ -6,6 +6,14 @@ export interface ServerProcess {
|
|
|
6
6
|
pid: number;
|
|
7
7
|
name: string;
|
|
8
8
|
command: string;
|
|
9
|
+
|
|
10
|
+
cpuPercent?: number | undefined;
|
|
11
|
+
memoryBytes?: number | undefined;
|
|
12
|
+
memoryPercent?: number | undefined;
|
|
13
|
+
status?: string | undefined;
|
|
14
|
+
threads?: number | undefined;
|
|
15
|
+
createTimeMs?: number | undefined;
|
|
16
|
+
username?: string | undefined;
|
|
9
17
|
}
|
|
10
18
|
|
|
11
19
|
export default interface ServerMonitorResponse {
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import ObjectID from "../ObjectID";
|
|
2
2
|
import MonitorEvaluationSummary from "../Monitor/MonitorEvaluationSummary";
|
|
3
|
+
import { JSONObject } from "../JSON";
|
|
4
|
+
import MetricCriteriaContext from "../Monitor/MetricMonitor/MetricCriteriaContext";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* One per-series match produced by a metric monitor with
|
|
8
|
+
* groupByAttributeKeys set. The criteria evaluator emits an entry per
|
|
9
|
+
* series that breached the threshold, and MonitorResource fans this
|
|
10
|
+
* out into one incident + one alert per (criteria, incident template,
|
|
11
|
+
* fingerprint) triple.
|
|
12
|
+
*/
|
|
13
|
+
export interface PerSeriesCriteriaMatch {
|
|
14
|
+
criteriaMetId: string;
|
|
15
|
+
fingerprint: string;
|
|
16
|
+
labels: JSONObject;
|
|
17
|
+
rootCause: string;
|
|
18
|
+
metricContext?: MetricCriteriaContext | undefined;
|
|
19
|
+
}
|
|
3
20
|
|
|
4
21
|
export default interface ProbeApiIngestResponse {
|
|
5
22
|
monitorId: ObjectID;
|
|
@@ -8,4 +25,12 @@ export default interface ProbeApiIngestResponse {
|
|
|
8
25
|
criteriaMetId?: string | undefined;
|
|
9
26
|
rootCause: string | null; // this is in markdown format
|
|
10
27
|
evaluationSummary?: MonitorEvaluationSummary | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Set when a metric monitor with group-by attributes produced one or
|
|
30
|
+
* more per-series matches. MonitorResource uses this to create one
|
|
31
|
+
* incident/alert per breaching series. When undefined (non-metric
|
|
32
|
+
* monitors or ungrouped metric monitors), the scalar `criteriaMetId`
|
|
33
|
+
* + `rootCause` still drive the legacy single-incident path.
|
|
34
|
+
*/
|
|
35
|
+
perSeriesMatches?: Array<PerSeriesCriteriaMatch> | undefined;
|
|
11
36
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface StatusPageLanguage {
|
|
2
|
+
code: string;
|
|
3
|
+
nativeName: string;
|
|
4
|
+
englishName: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_STATUS_PAGE_LANGUAGE: string = "en";
|
|
8
|
+
|
|
9
|
+
export const SUPPORTED_STATUS_PAGE_LANGUAGES: Array<StatusPageLanguage> = [
|
|
10
|
+
{ code: "en", nativeName: "English", englishName: "English" },
|
|
11
|
+
{ code: "de", nativeName: "Deutsch", englishName: "German" },
|
|
12
|
+
{ code: "fr", nativeName: "Français", englishName: "French" },
|
|
13
|
+
{ code: "es", nativeName: "Español", englishName: "Spanish" },
|
|
14
|
+
{ code: "it", nativeName: "Italiano", englishName: "Italian" },
|
|
15
|
+
{ code: "pt", nativeName: "Português", englishName: "Portuguese" },
|
|
16
|
+
{ code: "nl", nativeName: "Nederlands", englishName: "Dutch" },
|
|
17
|
+
{ code: "da", nativeName: "Dansk", englishName: "Danish" },
|
|
18
|
+
{ code: "no", nativeName: "Norsk", englishName: "Norwegian" },
|
|
19
|
+
{ code: "sv", nativeName: "Svenska", englishName: "Swedish" },
|
|
20
|
+
{ code: "ru", nativeName: "Русский", englishName: "Russian" },
|
|
21
|
+
{ code: "ja", nativeName: "日本語", englishName: "Japanese" },
|
|
22
|
+
{ code: "ko", nativeName: "한국어", englishName: "Korean" },
|
|
23
|
+
{ code: "zh", nativeName: "中文", englishName: "Chinese" },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
export const SUPPORTED_STATUS_PAGE_LANGUAGE_CODES: Array<string> =
|
|
27
|
+
SUPPORTED_STATUS_PAGE_LANGUAGES.map((language: StatusPageLanguage) => {
|
|
28
|
+
return language.code;
|
|
29
|
+
});
|