Browse Source

ref(crons): new merge/aggregate status functions without `MonitorBucketEnvMapping` (#82566)

introducing new functions to replace:
- `getAggregateStatus`
- `getAggregateStatusFromMultipleBuckets`
- `mergeEnvMappings`

since `checkinTimeline` doesn't need to know anything about environment,
we want to change these functions to take in `StatsBucket` instead of
`MonitorBucketEnvMapping` types

replacing usage + deleting old functions will come in following PRs
mia hsu 2 months ago
parent
commit
c11f4c0992

+ 23 - 1
static/app/views/monitors/components/timeline/utils/getAggregateStatus.spec.tsx

@@ -1,6 +1,9 @@
 import {CheckInStatus} from 'sentry/views/monitors/types';
 
-import {getAggregateStatus} from './getAggregateStatus';
+import {
+  getAggregateStatus,
+  getAggregateStatusFromStatsBucket,
+} from './getAggregateStatus';
 
 type StatusCounts = [
   in_progress: number,
@@ -18,6 +21,18 @@ export function generateEnvMapping(name: string, counts: StatusCounts) {
   };
 }
 
+export function generateStats(counts: StatusCounts) {
+  const [in_progress, ok, missed, timeout, error, unknown] = counts;
+  return {
+    in_progress,
+    ok,
+    missed,
+    timeout,
+    error,
+    unknown,
+  };
+}
+
 describe('getAggregateStatus', function () {
   it('aggregates correctly across multiple envs', function () {
     const envData = {
@@ -27,3 +42,10 @@ describe('getAggregateStatus', function () {
     expect(getAggregateStatus(envData)).toEqual(CheckInStatus.ERROR);
   });
 });
+
+describe('getAggregateStatusFromStatsBucket', function () {
+  it('aggregates correctly', function () {
+    const stats = generateStats([0, 1, 2, 0, 1, 0]);
+    expect(getAggregateStatusFromStatsBucket(stats)).toEqual(CheckInStatus.ERROR);
+  });
+});

+ 8 - 1
static/app/views/monitors/components/timeline/utils/getAggregateStatus.tsx

@@ -1,4 +1,4 @@
-import type {MonitorBucketEnvMapping} from '../types';
+import type {MonitorBucketEnvMapping, StatsBucket} from '../types';
 
 import {CHECKIN_STATUS_PRECEDENT} from './constants';
 
@@ -12,3 +12,10 @@ export function getAggregateStatus(envData: MonitorBucketEnvMapping) {
     return currentStatus;
   }, CHECKIN_STATUS_PRECEDENT[0]);
 }
+
+export function getAggregateStatusFromStatsBucket(stats: StatsBucket) {
+  return (
+    [...CHECKIN_STATUS_PRECEDENT].reverse().find(status => stats[status] > 0) ||
+    CHECKIN_STATUS_PRECEDENT[0]
+  );
+}

+ 28 - 1
static/app/views/monitors/components/timeline/utils/getAggregateStatusFromMultipleBuckets.spec.tsx

@@ -1,6 +1,9 @@
 import {CheckInStatus} from 'sentry/views/monitors/types';
 
-import {getAggregateStatusFromMultipleBuckets} from './getAggregateStatusFromMultipleBuckets';
+import {
+  getAggregateStatusFromMultipleBuckets,
+  getAggregateStatusFromMultipleStatsBuckets,
+} from './getAggregateStatusFromMultipleBuckets';
 
 type StatusCounts = [
   in_progress: number,
@@ -18,6 +21,18 @@ export function generateEnvMapping(name: string, counts: StatusCounts) {
   };
 }
 
+export function generateStats(counts: StatusCounts) {
+  const [in_progress, ok, missed, timeout, error, unknown] = counts;
+  return {
+    in_progress,
+    ok,
+    missed,
+    timeout,
+    error,
+    unknown,
+  };
+}
+
 describe('getAggregateStatusFromMultipleBuckets', function () {
   it('aggregates correctly across multiple envs', function () {
     const envData1 = generateEnvMapping('prod', [2, 1, 2, 1, 0, 0]);
@@ -29,3 +44,15 @@ describe('getAggregateStatusFromMultipleBuckets', function () {
     expect(status).toEqual(CheckInStatus.TIMEOUT);
   });
 });
+
+describe('getAggregateStatusFromMultipleStatsBuckets', function () {
+  it('aggregates correctly across multiple envs', function () {
+    const stats1 = generateStats([2, 1, 2, 1, 0, 0]);
+    const stats2 = generateStats([1, 2, 0, 0, 0, 0]);
+    const stats3 = generateStats([1, 1, 1, 3, 0, 0]);
+
+    const status = getAggregateStatusFromMultipleStatsBuckets([stats1, stats2, stats3]);
+
+    expect(status).toEqual(CheckInStatus.TIMEOUT);
+  });
+});

+ 22 - 2
static/app/views/monitors/components/timeline/utils/getAggregateStatusFromMultipleBuckets.tsx

@@ -1,7 +1,10 @@
-import type {MonitorBucketEnvMapping} from '../types';
+import type {MonitorBucketEnvMapping, StatsBucket} from '../types';
 
 import {CHECKIN_STATUS_PRECEDENT} from './constants';
-import {getAggregateStatus} from './getAggregateStatus';
+import {
+  getAggregateStatus,
+  getAggregateStatusFromStatsBucket,
+} from './getAggregateStatus';
 
 /**
  * Given multiple env buckets [{prod: {ok: 1, ...}, {prod: {ok: 0, ...}}]
@@ -21,3 +24,20 @@ export function getAggregateStatusFromMultipleBuckets(
       CHECKIN_STATUS_PRECEDENT[0]
     );
 }
+
+/**
+ * Given multiple stats buckets [{..., error: 1, unknown: 0}, {..., error: 0, unknown: 4}]
+ * returns the aggregate status across all buckets (unknown)
+ */
+export function getAggregateStatusFromMultipleStatsBuckets(statsArr: StatsBucket[]) {
+  return statsArr
+    .map(getAggregateStatusFromStatsBucket)
+    .reduce(
+      (aggregateStatus, currentStatus) =>
+        CHECKIN_STATUS_PRECEDENT.indexOf(currentStatus) >
+        CHECKIN_STATUS_PRECEDENT.indexOf(aggregateStatus)
+          ? currentStatus
+          : aggregateStatus,
+      CHECKIN_STATUS_PRECEDENT[0]
+    );
+}

+ 37 - 2
static/app/views/monitors/components/timeline/utils/mergeEnvMappings.spec.tsx

@@ -1,6 +1,30 @@
-import type {MonitorBucketEnvMapping} from 'sentry/views/monitors/components/timeline/types';
+import type {
+  MonitorBucketEnvMapping,
+  StatsBucket,
+} from 'sentry/views/monitors/components/timeline/types';
 
-import {mergeEnvMappings} from './mergeEnvMappings';
+import {mergeEnvMappings, mergeStats} from './mergeEnvMappings';
+
+type StatusCounts = [
+  in_progress: number,
+  ok: number,
+  missed: number,
+  timeout: number,
+  error: number,
+  unknown: number,
+];
+
+export function generateStats(counts: StatusCounts) {
+  const [in_progress, ok, missed, timeout, error, unknown] = counts;
+  return {
+    in_progress,
+    ok,
+    missed,
+    timeout,
+    error,
+    unknown,
+  };
+}
 
 describe('mergeEnvMappings', function () {
   it('merges two empty mappings', function () {
@@ -52,3 +76,14 @@ describe('mergeEnvMappings', function () {
     expect(mergedMapping).toEqual(expectedMerged);
   });
 });
+
+describe('mergeStats', function () {
+  it('merges two filled mappings', function () {
+    const statsA: StatsBucket = generateStats([0, 0, 1, 2, 1, 0]);
+    const statsB: StatsBucket = generateStats([2, 1, 1, 0, 2, 0]);
+    const expectedMerged: StatsBucket = generateStats([2, 1, 2, 2, 3, 0]);
+    const mergedStats = mergeStats(statsA, statsB);
+
+    expect(mergedStats).toEqual(expectedMerged);
+  });
+});

+ 11 - 0
static/app/views/monitors/components/timeline/utils/mergeEnvMappings.tsx

@@ -24,3 +24,14 @@ export function mergeEnvMappings(
     return mergedEnvs;
   }, {});
 }
+
+/**
+ * Combines job status counts
+ */
+export function mergeStats(statsA: StatsBucket, statsB: StatsBucket): StatsBucket {
+  const combinedStats = {} as StatsBucket;
+  for (const status of CHECKIN_STATUS_PRECEDENT) {
+    combinedStats[status] = (statsA[status] ?? 0) + (statsB[status] ?? 0);
+  }
+  return combinedStats;
+}