Browse Source

feat(dashboards): Expose crash fields in Release Health dataset (#34106)

Add support for crash* fields in the release
health dataset.
Shruthi 2 years ago
parent
commit
0f1811765f

+ 9 - 2
static/app/types/sessions.tsx

@@ -1,4 +1,8 @@
-import {AggregationOutputType, ColumnType} from 'sentry/utils/discover/fields';
+import {
+  AggregateParameter,
+  AggregationOutputType,
+  ColumnType,
+} from 'sentry/utils/discover/fields';
 
 export type SessionsMeta = {
   name: string;
@@ -20,10 +24,13 @@ export type SessionsOperation =
   | 'p50'
   | 'p75'
   | 'p95'
-  | 'p99';
+  | 'p99'
+  | 'crash_rate'
+  | 'crash_free_rate';
 
 export type SessionAggregationColumn = {
   columnTypes: string[];
   defaultValue: SessionsMeta['name'];
   outputType: AggregationOutputType | null;
+  parameters: Readonly<AggregateParameter[]>;
 };

+ 93 - 15
static/app/views/dashboardsV2/widgetBuilder/releaseWidget/fields.tsx

@@ -12,12 +12,12 @@ import {FieldValue, FieldValueKind} from 'sentry/views/eventsV2/table/types';
 export const SESSIONS_FIELDS: Readonly<Partial<Record<SessionField, SessionsMeta>>> = {
   [SessionField.SESSION]: {
     name: 'session',
-    operations: ['sum'],
+    operations: ['sum', 'crash_rate', 'crash_free_rate'],
     type: 'integer',
   },
   [SessionField.USER]: {
     name: 'user',
-    operations: ['count_unique'],
+    operations: ['count_unique', 'crash_rate', 'crash_free_rate'],
     type: 'string',
   },
   [SessionField.SESSION_DURATION]: {
@@ -34,41 +34,131 @@ export const SESSIONS_OPERATIONS: Readonly<
     columnTypes: ['integer'],
     defaultValue: SessionField.SESSION,
     outputType: 'integer',
+    parameters: [
+      {
+        kind: 'column',
+        columnTypes: ['integer'],
+        defaultValue: SessionField.SESSION,
+        required: true,
+      },
+    ],
   },
   count_unique: {
     columnTypes: ['string'],
     defaultValue: SessionField.USER,
     outputType: 'integer',
+    parameters: [
+      {
+        kind: 'column',
+        columnTypes: ['string'],
+        defaultValue: SessionField.USER,
+        required: true,
+      },
+    ],
+  },
+  crash_rate: {
+    columnTypes: ['integer', 'string'],
+    defaultValue: SessionField.SESSION,
+    outputType: 'percentage',
+    parameters: [
+      {
+        kind: 'column',
+        columnTypes: ['integer', 'string'],
+        defaultValue: SessionField.SESSION,
+        required: true,
+      },
+    ],
+  },
+  crash_free_rate: {
+    columnTypes: ['integer', 'string'],
+    defaultValue: SessionField.SESSION,
+    outputType: 'percentage',
+    parameters: [
+      {
+        kind: 'column',
+        columnTypes: ['integer', 'string'],
+        defaultValue: SessionField.SESSION,
+        required: true,
+      },
+    ],
   },
   avg: {
     columnTypes: ['duration'],
     defaultValue: SessionField.SESSION_DURATION,
     outputType: null,
+    parameters: [
+      {
+        kind: 'column',
+        columnTypes: ['duration'],
+        defaultValue: SessionField.SESSION_DURATION,
+        required: true,
+      },
+    ],
   },
   max: {
     columnTypes: ['duration'],
     defaultValue: SessionField.SESSION_DURATION,
     outputType: null,
+    parameters: [
+      {
+        kind: 'column',
+        columnTypes: ['duration'],
+        defaultValue: SessionField.SESSION_DURATION,
+        required: true,
+      },
+    ],
   },
   p50: {
     columnTypes: ['duration'],
     defaultValue: SessionField.SESSION_DURATION,
     outputType: null,
+    parameters: [
+      {
+        kind: 'column',
+        columnTypes: ['duration'],
+        defaultValue: SessionField.SESSION_DURATION,
+        required: true,
+      },
+    ],
   },
   p75: {
     columnTypes: ['duration'],
     defaultValue: SessionField.SESSION_DURATION,
     outputType: null,
+    parameters: [
+      {
+        kind: 'column',
+        columnTypes: ['duration'],
+        defaultValue: SessionField.SESSION_DURATION,
+        required: true,
+      },
+    ],
   },
   p95: {
     columnTypes: ['duration'],
     defaultValue: SessionField.SESSION_DURATION,
     outputType: null,
+    parameters: [
+      {
+        kind: 'column',
+        columnTypes: ['duration'],
+        defaultValue: SessionField.SESSION_DURATION,
+        required: true,
+      },
+    ],
   },
   p99: {
     columnTypes: ['duration'],
     defaultValue: SessionField.SESSION_DURATION,
     outputType: null,
+    parameters: [
+      {
+        kind: 'column',
+        columnTypes: ['duration'],
+        defaultValue: SessionField.SESSION_DURATION,
+        required: true,
+      },
+    ],
   },
 };
 
@@ -113,25 +203,13 @@ export function generateReleaseWidgetFieldOptions(
     .filter(operation => knownOperations.includes(operation))
     .sort((a, b) => a.localeCompare(b))
     .forEach(operation => {
-      const defaultField = SESSIONS_OPERATIONS[operation].defaultValue;
-
       fieldOptions[`function:${operation}`] = {
         label: `${operation}(${'\u2026'})`,
         value: {
           kind: FieldValueKind.FUNCTION,
           meta: {
             name: operation,
-            parameters: [
-              {
-                kind: 'column',
-                columnTypes: SESSIONS_OPERATIONS[operation].columnTypes,
-                required: true,
-                defaultValue: fieldNames.includes(defaultField)
-                  ? defaultField
-                  : fields.find(field => field.operations.includes(operation))?.name ??
-                    '',
-              },
-            ],
+            parameters: SESSIONS_OPERATIONS[operation].parameters.map(param => param),
           },
         },
       };

+ 9 - 6
static/app/views/eventsV2/table/columnEditCollection.tsx

@@ -26,6 +26,7 @@ import {getPointerPosition} from 'sentry/utils/touch';
 import {setBodyUserSelect, UserSelectValues} from 'sentry/utils/userselect';
 import {WidgetType} from 'sentry/views/dashboardsV2/types';
 import {FieldKey} from 'sentry/views/dashboardsV2/widgetBuilder/issueWidget/fields';
+import {SESSIONS_OPERATIONS} from 'sentry/views/dashboardsV2/widgetBuilder/releaseWidget/fields';
 
 import {generateFieldOptions} from '../utils';
 
@@ -547,12 +548,14 @@ class ColumnEditCollection extends React.Component<Props, State> {
       source === WidgetType.ISSUE
         ? 1
         : Math.max(
-            ...columns.map(col =>
-              col.kind === 'function' &&
-              AGGREGATIONS[col.function[0]].parameters.length === 2
-                ? 3
-                : 2
-            )
+            ...columns.map(col => {
+              if (col.kind !== 'function') {
+                return 2;
+              }
+              const operation =
+                AGGREGATIONS[col.function[0]] ?? SESSIONS_OPERATIONS[col.function[0]];
+              return operation.parameters.length === 2 ? 3 : 2;
+            })
           );
 
     return (

+ 14 - 12
static/app/views/eventsV2/table/queryField.tsx

@@ -23,6 +23,7 @@ import {
   QueryFieldValue,
   ValidateColumnTypes,
 } from 'sentry/utils/discover/fields';
+import {SESSIONS_OPERATIONS} from 'sentry/views/dashboardsV2/widgetBuilder/releaseWidget/fields';
 
 import ArithmeticInput from './arithmeticInput';
 import {FieldValue, FieldValueColumns, FieldValueKind} from './types';
@@ -642,20 +643,21 @@ class QueryField extends React.Component<Props> {
     if (skipParameterPlaceholder) {
       // if the selected field is a function and has parameters, we would like to display each value in separate columns.
       // Otherwise the field should be displayed in a column, taking up all available space and not displaying the "no parameter" field
-      if (
-        fieldValue.kind === 'function' &&
-        AGGREGATIONS[fieldValue.function[0]].parameters.length > 0
-      ) {
-        if (
-          containerColumns === 3 &&
-          AGGREGATIONS[fieldValue.function[0]].parameters.length === 1
-        ) {
-          gridColumnsQuantity = 2;
+      if (fieldValue.kind !== 'function') {
+        gridColumnsQuantity = 1;
+      } else {
+        const operation =
+          AGGREGATIONS[fieldValue.function[0]] ??
+          SESSIONS_OPERATIONS[fieldValue.function[0]];
+        if (operation.parameters.length > 0) {
+          if (containerColumns === 3 && operation.parameters.length === 1) {
+            gridColumnsQuantity = 2;
+          } else {
+            gridColumnsQuantity = containerColumns;
+          }
         } else {
-          gridColumnsQuantity = containerColumns;
+          gridColumnsQuantity = 1;
         }
-      } else {
-        gridColumnsQuantity = 1;
       }
     }
 

+ 34 - 0
tests/js/spec/views/dashboardsV2/widgetBuilder/metricWidget/fields.spec.tsx

@@ -73,6 +73,40 @@ describe('generateReleaseWidgetFieldOptions', function () {
           },
         },
       },
+      'function:crash_free_rate': {
+        label: 'crash_free_rate(…)',
+        value: {
+          kind: 'function',
+          meta: {
+            name: 'crash_free_rate',
+            parameters: [
+              {
+                columnTypes: ['integer', 'string'],
+                defaultValue: 'session',
+                kind: 'column',
+                required: true,
+              },
+            ],
+          },
+        },
+      },
+      'function:crash_rate': {
+        label: 'crash_rate(…)',
+        value: {
+          kind: 'function',
+          meta: {
+            name: 'crash_rate',
+            parameters: [
+              {
+                columnTypes: ['integer', 'string'],
+                defaultValue: 'session',
+                kind: 'column',
+                required: true,
+              },
+            ],
+          },
+        },
+      },
       'function:max': {
         label: 'max(…)',
         value: {