Browse Source

ref(contexts): Update Device Context to latest Relay spec (#41637)

https://develop.sentry.dev/sdk/event-payloads/contexts/#device-context
contains the up to date expected values for the device context - I
updated our types to reflect this. This also involves removing some
types that are no longer there (and that Relay does not understand).

Co-authored-by: Priscila Oliveira <priscila.oliveira@sentry.io>
Abhijeet Prasad 2 years ago
parent
commit
d77eba20e1

+ 23 - 31
static/app/components/events/contexts/device/getDeviceKnownDataDetails.spec.tsx

@@ -1,5 +1,7 @@
-import {deviceKnownDataValues} from 'sentry/components/events/contexts/device';
-import {getDeviceKnownDataDetails} from 'sentry/components/events/contexts/device/getDeviceKnownDataDetails';
+import {
+  deviceKnownDataValues,
+  getDeviceKnownDataDetails,
+} from 'sentry/components/events/contexts/device/getDeviceKnownDataDetails';
 
 import {deviceMockData} from './index.spec';
 
@@ -22,47 +24,37 @@ describe('getDeviceKnownDataDetails', function () {
     }
 
     expect(allKnownData).toEqual([
-      {subject: 'Name', value: ''},
-      {subject: 'Family', value: 'Android'},
-      {subject: 'CPU Description', value: undefined},
       {subject: 'Architecture', value: 'x86'},
       {subject: 'Battery Level', value: '100%'},
       {subject: 'Battery Status', value: undefined},
-      {subject: 'Orientation', value: 'portrait'},
-      {subject: 'Memory', value: undefined},
-      expect.objectContaining({subject: 'Memory Size'}),
-      expect.objectContaining({subject: 'Free Memory'}),
-      {subject: 'Usable Memory', value: undefined},
-      {subject: 'Low Memory', value: false},
-      expect.objectContaining({subject: 'Storage Size'}),
-      expect.objectContaining({subject: 'External Storage Size'}),
-      expect.objectContaining({subject: 'External Free Storage'}),
-      {
-        subject: 'Capacity',
-        value:
-          'Total: 779.3 MiB / Free: 485.2 MiB (External Total: 510.0 MiB / Free: 509.9 MiB)',
-      },
-      expect.objectContaining({subject: 'Free Storage'}),
-      {subject: 'Simulator', value: true},
       expect.objectContaining({subject: 'Boot Time'}),
-      {subject: 'Timezone', value: 'America/Los_Angeles'},
-      {subject: 'Device Type', value: undefined},
-      {subject: 'Architectures', value: ['x86']},
       {subject: 'Brand', value: 'google'},
       {subject: 'Charging', value: true},
-      {subject: 'Connection Type', value: undefined},
-      {subject: 'Id', value: undefined},
-      {subject: 'Language', value: undefined},
+      {subject: 'CPU Description', value: undefined},
+      {subject: 'Device Type', value: undefined},
+      {subject: 'Family', value: 'Android'},
+      expect.objectContaining({subject: 'Free Memory'}),
+      {subject: 'Low Memory', value: false},
       {subject: 'Manufacturer', value: 'Google'},
+      expect.objectContaining({subject: 'Memory Size'}),
+      expect.objectContaining({subject: 'Model'}),
+      {subject: 'Model Id', value: 'NYC'},
+      {subject: 'Name', value: ''},
       {subject: 'Online', value: true},
+      {subject: 'Orientation', value: 'portrait'},
       {subject: 'Screen Density', value: 3},
       {subject: 'Screen DPI', value: 480},
-      {subject: 'Screen Resolution', value: '1136x768'},
       {subject: 'Screen Height Pixels', value: 1136},
+      {subject: 'Screen Resolution', value: '1136x768'},
       {subject: 'Screen Width Pixels', value: 768},
-      expect.objectContaining({subject: 'Model'}),
-      {subject: 'Model Id', value: 'NYC'},
-      {subject: 'Rendered Model', value: undefined},
+      {subject: 'Simulator', value: true},
+      {subject: 'Usable Memory', value: undefined},
+      {subject: 'Memory', value: undefined},
+      {
+        subject: 'Capacity',
+        value:
+          'Total: 779.3 MiB / Free: 485.2 MiB (External Total: 510.0 MiB / Free: 509.9 MiB)',
+      },
     ]);
   });
 });

+ 40 - 92
static/app/components/events/contexts/device/getDeviceKnownDataDetails.tsx

@@ -1,23 +1,31 @@
 import {DeviceName} from 'sentry/components/deviceName';
 import FileSize from 'sentry/components/fileSize';
 import {t} from 'sentry/locale';
-import {Event} from 'sentry/types/event';
+import {DeviceContext, DeviceContextKey, Event} from 'sentry/types/event';
 import {defined} from 'sentry/utils';
 
-import {getFullLanguageDescription, getRelativeTimeFromEventDateCreated} from '../utils';
+import {getRelativeTimeFromEventDateCreated} from '../utils';
 
-import {DeviceData, DeviceKnownDataType} from './types';
 import {formatMemory, formatStorage} from './utils';
 
+export const deviceKnownDataValues = [
+  ...Object.values(DeviceContextKey),
+  // Added two general keys here to namespace the values
+  // tracks memory_size, free_memory, usable_memory
+  'memory',
+  // tracks storage_size, free_storage, external_storage_size, external_free_storage
+  'storage',
+];
+
 type Output = {
   subject: string;
   value?: React.ReactNode;
 };
 
 type Props = {
-  data: DeviceData;
+  data: DeviceContext;
   event: Event;
-  type: DeviceKnownDataType;
+  type: keyof typeof deviceKnownDataValues;
 };
 
 export function getDeviceKnownDataDetails({
@@ -26,22 +34,22 @@ export function getDeviceKnownDataDetails({
   type,
 }: Props): Output | undefined {
   switch (type) {
-    case DeviceKnownDataType.NAME:
+    case DeviceContextKey.NAME:
       return {
         subject: t('Name'),
         value: data.name,
       };
-    case DeviceKnownDataType.FAMILY:
+    case DeviceContextKey.FAMILY:
       return {
         subject: t('Family'),
         value: data.family,
       };
-    case DeviceKnownDataType.MODEL_ID:
+    case DeviceContextKey.MODEL_ID:
       return {
         subject: t('Model Id'),
         value: data.model_id,
       };
-    case DeviceKnownDataType.MODEL:
+    case DeviceContextKey.MODEL:
       return {
         subject: t('Model'),
         value:
@@ -51,37 +59,32 @@ export function getDeviceKnownDataDetails({
             />
           ) : undefined,
       };
-    case DeviceKnownDataType.RENDERED_MODEL:
-      return {
-        subject: t('Rendered Model'),
-        value: data.renderedModel,
-      };
-    case DeviceKnownDataType.CPU_DESCRIPTION:
+    case DeviceContextKey.CPU_DESCRIPTION:
       return {
         subject: t('CPU Description'),
         value: data.cpu_description,
       };
-    case DeviceKnownDataType.ARCH:
+    case DeviceContextKey.ARCH:
       return {
         subject: t('Architecture'),
         value: data.arch,
       };
-    case DeviceKnownDataType.BATTERY_LEVEL:
+    case DeviceContextKey.BATTERY_LEVEL:
       return {
         subject: t('Battery Level'),
         value: defined(data.battery_level) ? `${data.battery_level}%` : undefined,
       };
-    case DeviceKnownDataType.BATTERY_STATUS:
+    case DeviceContextKey.BATTERY_STATUS:
       return {
         subject: t('Battery Status'),
         value: data.battery_status,
       };
-    case DeviceKnownDataType.ORIENTATION:
+    case DeviceContextKey.ORIENTATION:
       return {
         subject: t('Orientation'),
         value: data.orientation,
       };
-    case DeviceKnownDataType.MEMORY:
+    case 'memory':
       const {memory_size, free_memory, usable_memory} = data;
       return {
         subject: t('Memory'),
@@ -90,7 +93,7 @@ export function getDeviceKnownDataDetails({
             ? formatMemory(memory_size, free_memory, usable_memory)
             : undefined,
       };
-    case DeviceKnownDataType.STORAGE:
+    case 'storage':
       const {storage_size, free_storage, external_storage_size, external_free_storage} =
         data;
       return {
@@ -105,40 +108,12 @@ export function getDeviceKnownDataDetails({
               )
             : undefined,
       };
-    case DeviceKnownDataType.FREE_STORAGE: {
-      return {
-        subject: t('Free Storage'),
-        value: data.free_storage ? <FileSize bytes={data.free_storage} /> : undefined,
-      };
-    }
-    case DeviceKnownDataType.STORAGE_SIZE: {
-      return {
-        subject: t('Storage Size'),
-        value: data.storage_size ? <FileSize bytes={data.storage_size} /> : undefined,
-      };
-    }
-    case DeviceKnownDataType.EXTERNAL_STORAGE_SIZE: {
-      return {
-        subject: t('External Storage Size'),
-        value: data.external_storage_size ? (
-          <FileSize bytes={data.external_storage_size} />
-        ) : undefined,
-      };
-    }
-    case DeviceKnownDataType.EXTERNAL_FREE_STORAGE: {
-      return {
-        subject: t('External Free Storage'),
-        value: data.external_free_storage ? (
-          <FileSize bytes={data.external_free_storage} />
-        ) : undefined,
-      };
-    }
-    case DeviceKnownDataType.SIMULATOR:
+    case DeviceContextKey.SIMULATOR:
       return {
         subject: t('Simulator'),
         value: data.simulator,
       };
-    case DeviceKnownDataType.BOOT_TIME:
+    case DeviceContextKey.BOOT_TIME:
       return {
         subject: t('Boot Time'),
         value: getRelativeTimeFromEventDateCreated(
@@ -146,99 +121,72 @@ export function getDeviceKnownDataDetails({
           data.boot_time
         ),
       };
-    case DeviceKnownDataType.TIMEZONE:
-      return {
-        subject: t('Timezone'),
-        value: data.timezone,
-      };
-    case DeviceKnownDataType.DEVICE_TYPE:
+    case DeviceContextKey.DEVICE_TYPE:
       return {
         subject: t('Device Type'),
         value: data.device_type,
       };
-    case DeviceKnownDataType.ARCHS:
-      return {
-        subject: t('Architectures'),
-        value: data.archs,
-      };
-    case DeviceKnownDataType.BRAND:
+    case DeviceContextKey.BRAND:
       return {
         subject: t('Brand'),
         value: data.brand,
       };
-    case DeviceKnownDataType.CHARGING:
+    case DeviceContextKey.CHARGING:
       return {
         subject: t('Charging'),
         value: data.charging,
       };
-    case DeviceKnownDataType.CONNECTION_TYPE:
-      return {
-        subject: t('Connection Type'),
-        value: data.connection_type,
-      };
-    case DeviceKnownDataType.ID:
-      return {
-        subject: t('Id'),
-        value: data.id,
-      };
-    case DeviceKnownDataType.LANGUAGE:
-      return {
-        subject: t('Language'),
-        value: defined(data.language)
-          ? getFullLanguageDescription(data.language)
-          : undefined,
-      };
-    case DeviceKnownDataType.LOW_MEMORY:
+    case DeviceContextKey.LOW_MEMORY:
       return {
         subject: t('Low Memory'),
         value: data.low_memory,
       };
-    case DeviceKnownDataType.FREE_MEMORY:
+    case DeviceContextKey.FREE_MEMORY:
       return {
         subject: t('Free Memory'),
         value: data.free_memory ? <FileSize bytes={data.free_memory} /> : undefined,
       };
-    case DeviceKnownDataType.MEMORY_SIZE:
+    case DeviceContextKey.MEMORY_SIZE:
       return {
         subject: t('Memory Size'),
         value: data.memory_size ? <FileSize bytes={data.memory_size} /> : undefined,
       };
-    case DeviceKnownDataType.USABLE_MEMORY:
+    case DeviceContextKey.USABLE_MEMORY:
       return {
         subject: t('Usable Memory'),
         value: data.usable_memory ? <FileSize bytes={data.usable_memory} /> : undefined,
       };
-    case DeviceKnownDataType.MANUFACTURER:
+    case DeviceContextKey.MANUFACTURER:
       return {
         subject: t('Manufacturer'),
         value: data.manufacturer,
       };
-    case DeviceKnownDataType.ONLINE:
+    case DeviceContextKey.ONLINE:
       return {
         subject: t('Online'),
         value: data.online,
       };
-    case DeviceKnownDataType.SCREEN_DENSITY:
+    case DeviceContextKey.SCREEN_DENSITY:
       return {
         subject: t('Screen Density'),
         value: data.screen_density,
       };
-    case DeviceKnownDataType.SCREEN_DPI:
+    case DeviceContextKey.SCREEN_DPI:
       return {
         subject: t('Screen DPI'),
         value: data.screen_dpi,
       };
-    case DeviceKnownDataType.SCREEN_HEIGHT_PIXELS:
+    case DeviceContextKey.SCREEN_HEIGHT_PIXELS:
       return {
         subject: t('Screen Height Pixels'),
         value: data.screen_height_pixels,
       };
-    case DeviceKnownDataType.SCREEN_RESOLUTION:
+    case DeviceContextKey.SCREEN_RESOLUTION:
       return {
         subject: t('Screen Resolution'),
         value: data.screen_resolution,
       };
-    case DeviceKnownDataType.SCREEN_WIDTH_PIXELS:
+    case DeviceContextKey.SCREEN_WIDTH_PIXELS:
       return {
         subject: t('Screen Width Pixels'),
         value: data.screen_width_pixels,

+ 3 - 6
static/app/components/events/contexts/device/index.spec.tsx

@@ -2,9 +2,9 @@ import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
 import {textWithMarkupMatcher} from 'sentry-test/utils';
 
 import {DeviceEventContext} from 'sentry/components/events/contexts/device';
-import {DeviceData} from 'sentry/components/events/contexts/device/types';
+import {DeviceContext} from 'sentry/types';
 
-export const deviceMockData = {
+export const deviceMockData: DeviceContext = {
   screen_resolution: '1136x768',
   orientation: 'portrait',
   family: 'Android',
@@ -23,7 +23,6 @@ export const deviceMockData = {
   screen_density: 3,
   type: 'device',
   charging: true,
-  locale: 'US',
   model_id: 'NYC',
   brand: 'google',
   storage_size: 817143808,
@@ -33,9 +32,7 @@ export const deviceMockData = {
   name: '', // redacted
   free_storage: 508784640,
   model: 'Android SDK built for x86',
-  uuid: 'abadcade-feed-dead-beef-baddadfeeded',
-  archs: ['x86'],
-} as unknown as DeviceData;
+};
 
 export const deviceContextMetaMockData = {
   name: {

+ 7 - 45
static/app/components/events/contexts/device/index.tsx

@@ -1,59 +1,21 @@
 import {Fragment} from 'react';
 
 import ContextBlock from 'sentry/components/events/contexts/contextBlock';
-import {Event} from 'sentry/types/event';
+import {DeviceContext, Event} from 'sentry/types/event';
 
 import {geKnownData, getUnknownData} from '../utils';
 
-import {getDeviceKnownDataDetails} from './getDeviceKnownDataDetails';
-import {DeviceData, DeviceKnownDataType} from './types';
+import {
+  deviceKnownDataValues,
+  getDeviceKnownDataDetails,
+} from './getDeviceKnownDataDetails';
 import {getInferredData} from './utils';
 
 type Props = {
-  data: DeviceData;
+  data: DeviceContext;
   event: Event;
 };
 
-export const deviceKnownDataValues = [
-  DeviceKnownDataType.NAME,
-  DeviceKnownDataType.FAMILY,
-  DeviceKnownDataType.CPU_DESCRIPTION,
-  DeviceKnownDataType.ARCH,
-  DeviceKnownDataType.BATTERY_LEVEL,
-  DeviceKnownDataType.BATTERY_STATUS,
-  DeviceKnownDataType.ORIENTATION,
-  DeviceKnownDataType.MEMORY,
-  DeviceKnownDataType.MEMORY_SIZE,
-  DeviceKnownDataType.FREE_MEMORY,
-  DeviceKnownDataType.USABLE_MEMORY,
-  DeviceKnownDataType.LOW_MEMORY,
-  DeviceKnownDataType.STORAGE_SIZE,
-  DeviceKnownDataType.EXTERNAL_STORAGE_SIZE,
-  DeviceKnownDataType.EXTERNAL_FREE_STORAGE,
-  DeviceKnownDataType.STORAGE,
-  DeviceKnownDataType.FREE_STORAGE,
-  DeviceKnownDataType.SIMULATOR,
-  DeviceKnownDataType.BOOT_TIME,
-  DeviceKnownDataType.TIMEZONE,
-  DeviceKnownDataType.DEVICE_TYPE,
-  DeviceKnownDataType.ARCHS,
-  DeviceKnownDataType.BRAND,
-  DeviceKnownDataType.CHARGING,
-  DeviceKnownDataType.CONNECTION_TYPE,
-  DeviceKnownDataType.ID,
-  DeviceKnownDataType.LANGUAGE,
-  DeviceKnownDataType.MANUFACTURER,
-  DeviceKnownDataType.ONLINE,
-  DeviceKnownDataType.SCREEN_DENSITY,
-  DeviceKnownDataType.SCREEN_DPI,
-  DeviceKnownDataType.SCREEN_RESOLUTION,
-  DeviceKnownDataType.SCREEN_HEIGHT_PIXELS,
-  DeviceKnownDataType.SCREEN_WIDTH_PIXELS,
-  DeviceKnownDataType.MODEL,
-  DeviceKnownDataType.MODEL_ID,
-  DeviceKnownDataType.RENDERED_MODEL,
-];
-
 const deviceIgnoredDataValues = [];
 
 export function DeviceEventContext({data, event}: Props) {
@@ -63,7 +25,7 @@ export function DeviceEventContext({data, event}: Props) {
   return (
     <Fragment>
       <ContextBlock
-        data={geKnownData<DeviceData, DeviceKnownDataType>({
+        data={geKnownData<DeviceContext, keyof typeof deviceKnownDataValues>({
           data: inferredData,
           meta,
           knownDataTypes: deviceKnownDataValues,

+ 0 - 81
static/app/components/events/contexts/device/types.tsx

@@ -1,81 +0,0 @@
-export enum DeviceKnownDataType {
-  ARCH = 'arch',
-  ARCHS = 'archs',
-  BATTERY_LEVEL = 'battery_level',
-  BATTERY_STATUS = 'battery_status',
-  BRAND = 'brand',
-  BOOT_TIME = 'boot_time',
-  CONNECTION_TYPE = 'connection_type',
-  CHARGING = 'charging',
-  CPU_DESCRIPTION = 'cpu_description',
-  DEVICE_TYPE = 'device_type',
-  EXTERNAL_STORAGE_SIZE = 'external_storage_size',
-  EXTERNAL_FREE_STORAGE = 'external_free_storage',
-  FAMILY = 'family',
-  FREE_STORAGE = 'free_storage',
-  FREE_MEMORY = 'free_memory',
-  ID = 'id',
-  LANGUAGE = 'language',
-  LOW_MEMORY = 'low_memory',
-  MANUFACTURER = 'manufacturer',
-  MODEL = 'model',
-  MODEL_ID = 'model_id',
-  MEMORY = 'memory',
-  MEMORY_SIZE = 'memory_size',
-  NAME = 'name',
-  ONLINE = 'online',
-  ORIENTATION = 'orientation',
-  RENDERED_MODEL = 'renderedModel',
-  SIMULATOR = 'simulator',
-  SCREEN_DENSITY = 'screen_density',
-  SCREEN_DPI = 'screen_dpi',
-  SCREEN_HEIGHT_PIXELS = 'screen_height_pixels',
-  SCREEN_RESOLUTION = 'screen_resolution',
-  SCREEN_WIDTH_PIXELS = 'screen_width_pixels',
-  STORAGE_SIZE = 'storage_size',
-  STORAGE = 'storage',
-  TIMEZONE = 'timezone',
-  USABLE_MEMORY = 'usable_memory',
-}
-
-// TODO(ts): add correct types
-export type DeviceData = {
-  device_type: string;
-  name: string;
-  arch?: string;
-  archs?: Array<string>;
-  battery_level?: number;
-  battery_status?: string;
-  boot_time?: string;
-  brand?: string;
-  charging?: boolean;
-  connection_type?: any;
-  cpu_description?: string;
-  external_free_storage?: number;
-  external_storage_size?: number;
-  family?: string;
-  free_memory?: number;
-  free_storage?: number;
-  id?: any;
-  language?: any;
-  low_memory?: boolean;
-  manufacturer?: string;
-  memory?: any;
-  memory_size?: number;
-  model?: string;
-  model_id?: string;
-  online?: boolean;
-  orientation?: string;
-  renderedModel?: any;
-  screen_density?: number;
-  screen_dpi?: number;
-  screen_height_pixels?: number;
-  screen_resolution?: string;
-  screen_width_pixels?: number;
-  simulator?: boolean;
-  storage?: any;
-  storage_size?: number;
-  timezone?: string;
-  type?: string;
-  usable_memory?: number;
-};

+ 9 - 10
static/app/components/events/contexts/device/utils.tsx

@@ -1,7 +1,6 @@
+import {DeviceContext, DeviceContextKey} from 'sentry/types';
 import {defined, formatBytesBase2} from 'sentry/utils';
 
-import {DeviceData, DeviceKnownDataType} from './types';
-
 export function formatMemory(
   memory_size: number,
   free_memory: number,
@@ -78,17 +77,17 @@ export const commonDisplayResolutions = {
   '3840x2160': '4K UHD',
 };
 
-export function getInferredData(data: DeviceData) {
-  const screenResolution = data[DeviceKnownDataType.SCREEN_RESOLUTION];
-  const screenWidth = data[DeviceKnownDataType.SCREEN_WIDTH_PIXELS];
-  const screenHeight = data[DeviceKnownDataType.SCREEN_HEIGHT_PIXELS];
+export function getInferredData(data: DeviceContext) {
+  const screenResolution = data[DeviceContextKey.SCREEN_RESOLUTION];
+  const screenWidth = data[DeviceContextKey.SCREEN_WIDTH_PIXELS];
+  const screenHeight = data[DeviceContextKey.SCREEN_HEIGHT_PIXELS];
 
   if (screenResolution) {
     const displayResolutionDescription = commonDisplayResolutions[screenResolution];
 
     const commonData = {
       ...data,
-      [DeviceKnownDataType.SCREEN_RESOLUTION]: displayResolutionDescription
+      [DeviceContextKey.SCREEN_RESOLUTION]: displayResolutionDescription
         ? `${screenResolution} (${displayResolutionDescription})`
         : screenResolution,
     };
@@ -99,8 +98,8 @@ export function getInferredData(data: DeviceData) {
       if (width && height) {
         return {
           ...commonData,
-          [DeviceKnownDataType.SCREEN_WIDTH_PIXELS]: Number(width),
-          [DeviceKnownDataType.SCREEN_HEIGHT_PIXELS]: Number(height),
+          [DeviceContextKey.SCREEN_WIDTH_PIXELS]: Number(width),
+          [DeviceContextKey.SCREEN_HEIGHT_PIXELS]: Number(height),
         };
       }
     }
@@ -114,7 +113,7 @@ export function getInferredData(data: DeviceData) {
 
     return {
       ...data,
-      [DeviceKnownDataType.SCREEN_RESOLUTION]: displayResolutionDescription
+      [DeviceContextKey.SCREEN_RESOLUTION]: displayResolutionDescription
         ? `${displayResolution} (${displayResolutionDescription})`
         : displayResolution,
     };

+ 0 - 33
static/app/components/events/contexts/utils.tsx

@@ -6,7 +6,6 @@ import moment from 'moment-timezone';
 import ContextData from 'sentry/components/contextData';
 import {t} from 'sentry/locale';
 import plugins from 'sentry/plugins';
-import ConfigStore from 'sentry/stores/configStore';
 import space from 'sentry/styles/space';
 import {Event, KeyValueListData} from 'sentry/types';
 import {defined} from 'sentry/utils';
@@ -74,38 +73,6 @@ export function getRelativeTimeFromEventDateCreated(
   );
 }
 
-// Typescript doesn't have types for DisplayNames yet and that's why the type assertion "any" is needed below.
-// There is currently an open PR that intends to introduce the types https://github.com/microsoft/TypeScript/pull/44022
-export function getFullLanguageDescription(locale: string) {
-  const sentryAppLanguageCode = ConfigStore.get('languageCode');
-
-  const [languageAbbreviation, countryAbbreviation] = locale.includes('_')
-    ? locale.split('_')
-    : locale.split('-');
-
-  try {
-    const languageNames = new (Intl as any).DisplayNames(sentryAppLanguageCode, {
-      type: 'language',
-    });
-
-    const languageName = languageNames.of(languageAbbreviation);
-
-    if (countryAbbreviation) {
-      const regionNames = new (Intl as any).DisplayNames(sentryAppLanguageCode, {
-        type: 'region',
-      });
-
-      const countryName = regionNames.of(countryAbbreviation.toUpperCase());
-
-      return `${languageName} (${countryName})`;
-    }
-
-    return languageName;
-  } catch {
-    return locale;
-  }
-}
-
 export function geKnownData<Data, DataType>({
   data,
   knownDataTypes,

+ 5 - 3
static/app/components/events/interfaces/frame/contexts.spec.tsx

@@ -4,6 +4,7 @@ import {DeviceEventContext} from 'sentry/components/events/contexts/device';
 import {commonDisplayResolutions} from 'sentry/components/events/contexts/device/utils';
 import {UserEventContext} from 'sentry/components/events/contexts/user';
 import {FILTER_MASK} from 'sentry/constants';
+import {DeviceContext} from 'sentry/types';
 
 describe('User', function () {
   it("displays filtered values but doesn't use them for avatar", function () {
@@ -63,7 +64,8 @@ describe('User', function () {
 });
 
 describe('Device', function () {
-  const device = {
+  const device: DeviceContext = {
+    type: 'device',
     name: 'Device Name',
     screen_resolution: '3840x2160',
     screen_width_pixels: 3840,
@@ -100,7 +102,7 @@ describe('Device', function () {
         screen.getByTestId('device-context-screen_resolution-value')
       ).toHaveTextContent(
         `${device.screen_resolution} (${
-          commonDisplayResolutions[device.screen_resolution]
+          commonDisplayResolutions[String(device.screen_resolution)]
         })`
       );
     });
@@ -129,7 +131,7 @@ describe('Device', function () {
         screen.getByTestId('device-context-screen_resolution-value')
       ).toHaveTextContent(
         `${device.screen_resolution} (${
-          commonDisplayResolutions[device.screen_resolution]
+          commonDisplayResolutions[String(device.screen_resolution)]
         })`
       );
     });

+ 96 - 8
static/app/types/event.tsx

@@ -357,7 +357,102 @@ export type Entry =
   | EntryGeneric
   | EntryResources;
 
-// Contexts
+// Contexts: https://develop.sentry.dev/sdk/event-payloads/contexts/
+
+export interface BaseContext {
+  type: string;
+}
+
+export enum DeviceContextKey {
+  ARCH = 'arch',
+  BATTERY_LEVEL = 'battery_level',
+  BATTERY_STATUS = 'battery_status',
+  BOOT_TIME = 'boot_time',
+  BRAND = 'brand',
+  CHARGING = 'charging',
+  CPU_DESCRIPTION = 'cpu_description',
+  DEVICE_TYPE = 'device_type',
+  DEVICE_UNIQUE_IDENTIFIER = 'device_unique_identifier',
+  EXTERNAL_FREE_STORAGE = 'external_free_storage',
+  EXTERNAL_STORAGE_SIZE = 'external_storage_size',
+  EXTERNAL_TOTAL_STORAGE = 'external_total_storage',
+  FAMILY = 'family',
+  FREE_MEMORY = 'free_memory',
+  FREE_STORAGE = 'free_storage',
+  LOW_MEMORY = 'low_memory',
+  MANUFACTURER = 'manufacturer',
+  MEMORY_SIZE = 'memory_size',
+  MODEL = 'model',
+  MODEL_ID = 'model_id',
+  NAME = 'name',
+  ONLINE = 'online',
+  ORIENTATION = 'orientation',
+  PROCESSOR_COUNT = 'processor_count',
+  PROCESSOR_FREQUENCY = 'processor_frequency',
+  SCREEN_DENSITY = 'screen_density',
+  SCREEN_DPI = 'screen_dpi',
+  SCREEN_HEIGHT_PIXELS = 'screen_height_pixels',
+  SCREEN_RESOLUTION = 'screen_resolution',
+  SCREEN_WIDTH_PIXELS = 'screen_width_pixels',
+  SIMULATOR = 'simulator',
+  STORAGE_SIZE = 'storage_size',
+  SUPPORTS_ACCELEROMETER = 'supports_accelerometer',
+  SUPPORTS_AUDIO = 'supports_audio',
+  SUPPORTS_GYROSCOPE = 'supports_gyroscope',
+  SUPPORTS_LOCATION_SERVICE = 'supports_location_service',
+  SUPPORTS_VIBRATION = 'supports_vibration',
+  USABLE_MEMORY = 'usable_memory',
+}
+
+// https://develop.sentry.dev/sdk/event-payloads/contexts/#device-context
+export interface DeviceContext
+  extends Partial<Record<DeviceContextKey, unknown>>,
+    BaseContext {
+  type: 'device';
+  [DeviceContextKey.NAME]: string;
+  [DeviceContextKey.ARCH]?: string;
+  [DeviceContextKey.BATTERY_LEVEL]?: number;
+  [DeviceContextKey.BATTERY_STATUS]?: string;
+  [DeviceContextKey.BOOT_TIME]?: string;
+  [DeviceContextKey.BRAND]?: string;
+  [DeviceContextKey.CHARGING]?: boolean;
+  [DeviceContextKey.CPU_DESCRIPTION]?: string;
+  [DeviceContextKey.DEVICE_TYPE]?: string;
+  [DeviceContextKey.DEVICE_UNIQUE_IDENTIFIER]?: string;
+  [DeviceContextKey.EXTERNAL_FREE_STORAGE]?: number;
+  [DeviceContextKey.EXTERNAL_STORAGE_SIZE]?: number;
+  [DeviceContextKey.EXTERNAL_TOTAL_STORAGE]?: number;
+  [DeviceContextKey.FAMILY]?: string;
+  [DeviceContextKey.FREE_MEMORY]?: number;
+  [DeviceContextKey.FREE_STORAGE]?: number;
+  [DeviceContextKey.LOW_MEMORY]?: boolean;
+  [DeviceContextKey.MANUFACTURER]?: string;
+  [DeviceContextKey.MEMORY_SIZE]?: number;
+  [DeviceContextKey.MODEL]?: string;
+  [DeviceContextKey.MODEL_ID]?: string;
+  [DeviceContextKey.ONLINE]?: boolean;
+  [DeviceContextKey.ORIENTATION]?: 'portrait' | 'landscape';
+  [DeviceContextKey.PROCESSOR_COUNT]?: number;
+  [DeviceContextKey.PROCESSOR_FREQUENCY]?: number;
+  [DeviceContextKey.SCREEN_DENSITY]?: number;
+  [DeviceContextKey.SCREEN_DPI]?: number;
+  [DeviceContextKey.SCREEN_HEIGHT_PIXELS]?: number;
+  [DeviceContextKey.SCREEN_RESOLUTION]?: string;
+  [DeviceContextKey.SCREEN_WIDTH_PIXELS]?: number;
+  [DeviceContextKey.SIMULATOR]?: boolean;
+  [DeviceContextKey.STORAGE_SIZE]?: number;
+  [DeviceContextKey.SUPPORTS_ACCELEROMETER]?: boolean;
+  [DeviceContextKey.SUPPORTS_AUDIO]?: boolean;
+  [DeviceContextKey.SUPPORTS_GYROSCOPE]?: boolean;
+  [DeviceContextKey.SUPPORTS_LOCATION_SERVICE]?: boolean;
+  [DeviceContextKey.SUPPORTS_VIBRATION]?: boolean;
+  [DeviceContextKey.USABLE_MEMORY]?: number;
+  // This field is deprecated in favour of locale field in culture context
+  language?: string;
+  // This field is deprecated in favour of timezone field in culture context
+  timezone?: string;
+}
+
 type RuntimeContext = {
   type: 'runtime';
   build?: string;
@@ -366,13 +461,6 @@ type RuntimeContext = {
   version?: number;
 };
 
-type DeviceContext = {
-  arch: string;
-  family: string;
-  model: string;
-  type: string;
-};
-
 type OSContext = {
   build: string;
   kernel_version: string;