index.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /* global process */
  2. import {t} from 'sentry/locale';
  3. import type {DataCategoryInfo, OrgRole, PermissionResource, Scope} from 'sentry/types';
  4. import {DataCategoryExact} from 'sentry/types';
  5. /**
  6. * Common constants here
  7. */
  8. // This is the element id where we render our React application to
  9. export const ROOT_ELEMENT = 'blk_router';
  10. export const USING_CUSTOMER_DOMAIN =
  11. typeof window !== 'undefined' ? Boolean(window?.__initialData?.customerDomain) : false;
  12. export const CUSTOMER_DOMAIN =
  13. typeof window !== 'undefined'
  14. ? window?.__initialData?.customerDomain?.subdomain
  15. : undefined;
  16. // This is considered the "default" route/view that users should be taken
  17. // to when the application does not have any further context
  18. //
  19. // e.g. loading app root or switching organization
  20. export const DEFAULT_APP_ROUTE = USING_CUSTOMER_DOMAIN
  21. ? '/issues/'
  22. : '/organizations/:orgSlug/issues/';
  23. export const API_ACCESS_SCOPES = [
  24. 'alerts:read',
  25. 'alerts:write',
  26. 'event:admin',
  27. 'event:read',
  28. 'event:write',
  29. 'member:admin',
  30. 'member:read',
  31. 'member:write',
  32. 'org:admin',
  33. 'org:integrations',
  34. 'org:read',
  35. 'org:write',
  36. 'project:admin',
  37. 'project:read',
  38. 'project:releases',
  39. 'project:write',
  40. 'team:admin',
  41. 'team:read',
  42. 'team:write',
  43. ] as const;
  44. export const ALLOWED_SCOPES = [
  45. 'alerts:read',
  46. 'alerts:write',
  47. 'event:admin',
  48. 'event:read',
  49. 'event:write',
  50. 'member:admin',
  51. 'member:read',
  52. 'member:write',
  53. 'org:admin',
  54. 'org:billing',
  55. 'org:integrations',
  56. 'org:read',
  57. 'org:superuser', // not an assignable API access scope
  58. 'org:write',
  59. 'project:admin',
  60. 'project:read',
  61. 'project:releases',
  62. 'project:write',
  63. 'team:admin',
  64. 'team:read',
  65. 'team:write',
  66. ] as const;
  67. // These should only be used in the case where we cannot obtain roles through
  68. // the members endpoint (primarily in cases where a user is admining a
  69. // different organization they are not a OrganizationMember of ).
  70. export const ORG_ROLES: OrgRole[] = [
  71. {
  72. id: 'member',
  73. name: 'Member',
  74. isAllowed: true,
  75. desc: 'Members can view and act on events, as well as view most other data within the organization.',
  76. minimumTeamRole: 'contributor',
  77. isTeamRolesAllowed: true,
  78. },
  79. {
  80. id: 'admin',
  81. name: 'Admin',
  82. isAllowed: true,
  83. desc: "Admin privileges on any teams of which they're a member. They can create new teams and projects, as well as remove teams and projects on which they already hold membership (or all teams, if open membership is enabled). Additionally, they can manage memberships of teams that they are members of. They cannot invite members to the organization.",
  84. minimumTeamRole: 'admin',
  85. isTeamRolesAllowed: true,
  86. },
  87. {
  88. id: 'manager',
  89. name: 'Manager',
  90. isAllowed: true,
  91. desc: 'Gains admin access on all teams as well as the ability to add and remove members.',
  92. minimumTeamRole: 'admin',
  93. isTeamRolesAllowed: true,
  94. },
  95. {
  96. id: 'owner',
  97. name: 'Organization Owner',
  98. isAllowed: true,
  99. desc: 'Unrestricted access to the organization, its data, and its settings. Can add, modify, and delete projects and members, as well as make billing and plan changes.',
  100. minimumTeamRole: 'admin',
  101. isTeamRolesAllowed: true,
  102. },
  103. ];
  104. export type PermissionChoice = {
  105. label: 'No Access' | 'Read' | 'Read & Write' | 'Admin';
  106. scopes: Scope[];
  107. };
  108. type PermissionObj = {
  109. choices: {
  110. admin: PermissionChoice;
  111. 'no-access': PermissionChoice;
  112. read?: PermissionChoice;
  113. write?: PermissionChoice;
  114. };
  115. help: string;
  116. resource: PermissionResource;
  117. label?: string;
  118. };
  119. export const RELEASE_ADOPTION_STAGES = ['low_adoption', 'adopted', 'replaced'];
  120. // We expose permissions for Sentry Apps in a more resource-centric way.
  121. // All of the API_ACCESS_SCOPES from above should be represented in a more
  122. // User-friendly way here.
  123. export const SENTRY_APP_PERMISSIONS: PermissionObj[] = [
  124. {
  125. resource: 'Project',
  126. help: 'Projects, Tags, Debug Files, and Feedback',
  127. choices: {
  128. 'no-access': {label: 'No Access', scopes: []},
  129. read: {label: 'Read', scopes: ['project:read']},
  130. write: {label: 'Read & Write', scopes: ['project:read', 'project:write']},
  131. admin: {label: 'Admin', scopes: ['project:read', 'project:write', 'project:admin']},
  132. },
  133. },
  134. {
  135. resource: 'Team',
  136. help: 'Teams of members',
  137. choices: {
  138. 'no-access': {label: 'No Access', scopes: []},
  139. read: {label: 'Read', scopes: ['team:read']},
  140. write: {label: 'Read & Write', scopes: ['team:read', 'team:write']},
  141. admin: {label: 'Admin', scopes: ['team:read', 'team:write', 'team:admin']},
  142. },
  143. },
  144. {
  145. resource: 'Release',
  146. help: 'Releases, Commits, and related Files',
  147. choices: {
  148. 'no-access': {label: 'No Access', scopes: []},
  149. admin: {label: 'Admin', scopes: ['project:releases']},
  150. },
  151. },
  152. {
  153. resource: 'Event',
  154. label: 'Issue & Event',
  155. help: 'Issues, Events, and workflow statuses',
  156. choices: {
  157. 'no-access': {label: 'No Access', scopes: []},
  158. read: {label: 'Read', scopes: ['event:read']},
  159. write: {label: 'Read & Write', scopes: ['event:read', 'event:write']},
  160. admin: {label: 'Admin', scopes: ['event:read', 'event:write', 'event:admin']},
  161. },
  162. },
  163. {
  164. resource: 'Organization',
  165. help: 'Manage Organizations, resolve IDs, retrieve Repositories and Commits',
  166. choices: {
  167. 'no-access': {label: 'No Access', scopes: []},
  168. read: {label: 'Read', scopes: ['org:read']},
  169. write: {label: 'Read & Write', scopes: ['org:read', 'org:write']},
  170. admin: {
  171. label: 'Admin',
  172. scopes: ['org:read', 'org:write', 'org:admin', 'org:integrations'],
  173. },
  174. },
  175. },
  176. {
  177. resource: 'Member',
  178. help: 'Manage Members within Teams',
  179. choices: {
  180. 'no-access': {label: 'No Access', scopes: []},
  181. read: {label: 'Read', scopes: ['member:read']},
  182. write: {label: 'Read & Write', scopes: ['member:read', 'member:write']},
  183. admin: {label: 'Admin', scopes: ['member:read', 'member:write', 'member:admin']},
  184. },
  185. },
  186. ];
  187. export const DEFAULT_TOAST_DURATION = 6000;
  188. export const DEFAULT_DEBOUNCE_DURATION = 300;
  189. export const ALL_ENVIRONMENTS_KEY = '__all_environments__';
  190. // Maps a `type: string` -> `url-prefix: string`
  191. export const AVATAR_URL_MAP = {
  192. team: 'team-avatar',
  193. organization: 'organization-avatar',
  194. project: 'project-avatar',
  195. user: 'avatar',
  196. sentryAppColor: 'sentry-app-avatar',
  197. sentryAppSimple: 'sentry-app-avatar',
  198. docIntegration: 'doc-integration-avatar',
  199. };
  200. export const MENU_CLOSE_DELAY = 200;
  201. export const SLOW_TOOLTIP_DELAY = 1000;
  202. export const MAX_PICKABLE_DAYS = 90;
  203. export const DEFAULT_STATS_PERIOD = '14d';
  204. export const DEFAULT_QUERY = 'is:unresolved';
  205. export const NEW_DEFAULT_QUERY = 'is:unresolved issue.priority:[high, medium]';
  206. export const DEFAULT_USE_UTC = true;
  207. export const DEFAULT_RELATIVE_PERIODS = {
  208. '1h': t('Last hour'),
  209. '24h': t('Last 24 hours'),
  210. '7d': t('Last 7 days'),
  211. '14d': t('Last 14 days'),
  212. '30d': t('Last 30 days'),
  213. '90d': t('Last 90 days'),
  214. };
  215. export const DEFAULT_RELATIVE_PERIODS_PAGE_FILTER = {
  216. '1h': t('1H'),
  217. '24h': t('24H'),
  218. '7d': t('7D'),
  219. '14d': t('14D'),
  220. '30d': t('30D'),
  221. };
  222. // https://github.com/getsentry/relay/blob/master/relay-base-schema/src/data_category.rs
  223. export const DATA_CATEGORY_INFO = {
  224. [DataCategoryExact.ERROR]: {
  225. name: DataCategoryExact.ERROR,
  226. apiName: 'error',
  227. plural: 'errors',
  228. displayName: 'error',
  229. titleName: t('Errors'),
  230. uid: 1,
  231. },
  232. [DataCategoryExact.TRANSACTION]: {
  233. name: DataCategoryExact.TRANSACTION,
  234. apiName: 'transaction',
  235. plural: 'transactions',
  236. displayName: 'transaction',
  237. titleName: t('Transactions'),
  238. uid: 2,
  239. },
  240. [DataCategoryExact.ATTACHMENT]: {
  241. name: DataCategoryExact.ATTACHMENT,
  242. apiName: 'attachment',
  243. plural: 'attachments',
  244. displayName: 'attachment',
  245. titleName: t('Attachments'),
  246. uid: 4,
  247. },
  248. [DataCategoryExact.PROFILE]: {
  249. name: DataCategoryExact.PROFILE,
  250. apiName: 'profile',
  251. plural: 'profiles',
  252. displayName: 'profile',
  253. titleName: t('Profiles'),
  254. uid: 6,
  255. },
  256. [DataCategoryExact.REPLAY]: {
  257. name: DataCategoryExact.REPLAY,
  258. apiName: 'replay',
  259. plural: 'replays',
  260. displayName: 'replay',
  261. titleName: t('Session Replays'),
  262. uid: 7,
  263. },
  264. [DataCategoryExact.TRANSACTION_PROCESSED]: {
  265. name: DataCategoryExact.TRANSACTION_PROCESSED,
  266. apiName: 'transactions',
  267. plural: 'transactions',
  268. displayName: 'transaction',
  269. titleName: t('Transactions'),
  270. uid: 8,
  271. },
  272. [DataCategoryExact.TRANSACTION_INDEXED]: {
  273. name: DataCategoryExact.TRANSACTION_INDEXED,
  274. apiName: 'transactionIndexed',
  275. plural: 'indexed transactions',
  276. displayName: 'indexed transaction',
  277. titleName: t('Indexed Transactions'),
  278. uid: 9,
  279. },
  280. [DataCategoryExact.MONITOR]: {
  281. name: DataCategoryExact.MONITOR,
  282. apiName: 'monitor',
  283. plural: 'monitor check-ins',
  284. displayName: 'monitor check-in',
  285. titleName: t('Monitor Check-Ins'),
  286. uid: 10,
  287. },
  288. [DataCategoryExact.MONITOR_SEAT]: {
  289. name: DataCategoryExact.MONITOR_SEAT,
  290. apiName: 'monitorSeat',
  291. plural: 'monitorSeats',
  292. displayName: 'cron monitors',
  293. titleName: t('Cron Monitors'),
  294. uid: 13,
  295. },
  296. } as const satisfies Record<DataCategoryExact, DataCategoryInfo>;
  297. // Special Search characters
  298. export const NEGATION_OPERATOR = '!';
  299. export const SEARCH_WILDCARD = '*';
  300. // SmartSearchBar settings
  301. export const MAX_AUTOCOMPLETE_RECENT_SEARCHES = 3;
  302. export const MAX_AUTOCOMPLETE_RELEASES = 5;
  303. export const DEFAULT_PER_PAGE = 50;
  304. // Limit query length so paginated response headers don't
  305. // go over HTTP header size limits (4Kb)
  306. export const MAX_QUERY_LENGTH = 400;
  307. // Webpack configures DEPLOY_PREVIEW_CONFIG for deploy preview builds.
  308. export const DEPLOY_PREVIEW_CONFIG = process.env.DEPLOY_PREVIEW_CONFIG as unknown as
  309. | undefined
  310. | {
  311. branch: string;
  312. commitSha: string;
  313. githubOrg: string;
  314. githubRepo: string;
  315. };
  316. // Webpack configures EXPERIMENTAL_SPA.
  317. export const EXPERIMENTAL_SPA = process.env.EXPERIMENTAL_SPA as unknown as
  318. | undefined
  319. | boolean;
  320. // so we don't use filtered values in certain display contexts
  321. // TODO(kmclb): once relay is doing the scrubbing, the masking value will be dynamic,
  322. // so this will have to change
  323. export const FILTER_MASK = '[Filtered]';
  324. // Errors that may occur during the fetching of organization details
  325. export const ORGANIZATION_FETCH_ERROR_TYPES = {
  326. ORG_NOT_FOUND: 'ORG_NOT_FOUND',
  327. ORG_NO_ACCESS: 'ORG_NO_ACCESS',
  328. NO_ORGS: 'NO_ORGS',
  329. };
  330. export const CONFIG_DOCS_URL = 'https://develop.sentry.dev/config/';
  331. export const DISCOVER2_DOCS_URL = 'https://docs.sentry.io/product/discover-queries/';
  332. export const IS_ACCEPTANCE_TEST = !!process.env.IS_ACCEPTANCE_TEST;
  333. export const NODE_ENV = process.env.NODE_ENV;
  334. export const SPA_DSN = process.env.SPA_DSN;
  335. export const SENTRY_RELEASE_VERSION = process.env.SENTRY_RELEASE_VERSION;
  336. export const UI_DEV_ENABLE_PROFILING = process.env.UI_DEV_ENABLE_PROFILING;
  337. export const USE_REACT_QUERY_DEVTOOL = process.env.USE_REACT_QUERY_DEVTOOL;
  338. export const DEFAULT_ERROR_JSON = {
  339. detail: t('Unknown error. Please try again.'),
  340. };