123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- import {useMemo} from 'react';
- import orderBy from 'lodash/orderBy';
- import {bulkUpdate, useFetchIssueTags} from 'sentry/actionCreators/group';
- import {Client} from 'sentry/api';
- import {t} from 'sentry/locale';
- import ConfigStore from 'sentry/stores/configStore';
- import {useLegacyStore} from 'sentry/stores/useLegacyStore';
- import type {Group, GroupActivity, TagValue} from 'sentry/types';
- import type {Event} from 'sentry/types/event';
- import {useLocation} from 'sentry/utils/useLocation';
- import useOrganization from 'sentry/utils/useOrganization';
- export function markEventSeen(
- api: Client,
- orgId: string,
- projectId: string,
- groupId: string
- ) {
- bulkUpdate(
- api,
- {
- orgId,
- projectId,
- itemIds: [groupId],
- failSilently: true,
- data: {hasSeen: true},
- },
- {}
- );
- }
- export function fetchGroupUserReports(
- orgSlug: string,
- groupId: string,
- query: Record<string, string>
- ) {
- const api = new Client();
- return api.requestPromise(`/organizations/${orgSlug}/issues/${groupId}/user-reports/`, {
- includeAllArgs: true,
- query,
- });
- }
- export function useDefaultIssueEvent() {
- const user = useLegacyStore(ConfigStore).user;
- const options = user ? user.options : null;
- return options?.defaultIssueEvent ?? 'recommended';
- }
- /**
- * Combines two TagValue arrays and combines TagValue.count upon conflict
- */
- export function mergeAndSortTagValues(
- tagValues1: TagValue[],
- tagValues2: TagValue[],
- sort: 'count' | 'lastSeen' = 'lastSeen'
- ): TagValue[] {
- const tagValueCollection = tagValues1.reduce<Record<string, TagValue>>(
- (acc, tagValue) => {
- acc[tagValue.value] = tagValue;
- return acc;
- },
- {}
- );
- tagValues2.forEach(tagValue => {
- if (tagValueCollection[tagValue.value]) {
- tagValueCollection[tagValue.value].count += tagValue.count;
- if (tagValue.lastSeen > tagValueCollection[tagValue.value].lastSeen) {
- tagValueCollection[tagValue.value].lastSeen = tagValue.lastSeen;
- }
- } else {
- tagValueCollection[tagValue.value] = tagValue;
- }
- });
- const allTagValues: TagValue[] = Object.values(tagValueCollection);
- if (sort === 'count') {
- allTagValues.sort((a, b) => b.count - a.count);
- } else {
- allTagValues.sort((a, b) => (b.lastSeen < a.lastSeen ? -1 : 1));
- }
- return allTagValues;
- }
- /**
- * Returns the environment name for an event or null
- *
- * @param event
- */
- export function getEventEnvironment(event: Event) {
- const tag = event.tags.find(({key}) => key === 'environment');
- return tag ? tag.value : null;
- }
- const SUBSCRIPTION_REASONS = {
- commented: t(
- "You're receiving workflow notifications because you have commented on this issue."
- ),
- assigned: t(
- "You're receiving workflow notifications because you were assigned to this issue."
- ),
- bookmarked: t(
- "You're receiving workflow notifications because you have bookmarked this issue."
- ),
- changed_status: t(
- "You're receiving workflow notifications because you have changed the status of this issue."
- ),
- mentioned: t(
- "You're receiving workflow notifications because you have been mentioned in this issue."
- ),
- };
- /**
- * @param group
- * @param removeLinks add/remove links to subscription reasons text (default: false)
- * @returns Reason for subscription
- */
- export function getSubscriptionReason(group: Group) {
- if (group.subscriptionDetails?.disabled) {
- return t('You have disabled workflow notifications for this project.');
- }
- if (!group.isSubscribed) {
- return t('Subscribe to workflow notifications for this issue');
- }
- if (group.subscriptionDetails) {
- const {reason} = group.subscriptionDetails;
- if (reason === 'unknown') {
- return t(
- "You're receiving workflow notifications because you are subscribed to this issue."
- );
- }
- if (reason && SUBSCRIPTION_REASONS.hasOwnProperty(reason)) {
- return SUBSCRIPTION_REASONS[reason];
- }
- }
- return t(
- "You're receiving updates because you are subscribed to workflow notifications for this project."
- );
- }
- export function getGroupMostRecentActivity(
- activities: GroupActivity[] | undefined
- ): GroupActivity | undefined {
- // Most recent activity
- return activities
- ? orderBy([...activities], ({dateCreated}) => new Date(dateCreated), ['desc'])[0]
- : undefined;
- }
- export enum ReprocessingStatus {
- REPROCESSED_AND_HASNT_EVENT = 'reprocessed_and_hasnt_event',
- REPROCESSED_AND_HAS_EVENT = 'reprocessed_and_has_event',
- REPROCESSING = 'reprocessing',
- NO_STATUS = 'no_status',
- }
- // Reprocessing Checks
- export function getGroupReprocessingStatus(
- group: Group,
- mostRecentActivity?: GroupActivity
- ) {
- const {status, count, activity: activities} = group;
- const groupCount = Number(count);
- switch (status) {
- case 'reprocessing':
- return ReprocessingStatus.REPROCESSING;
- case 'unresolved': {
- const groupMostRecentActivity =
- mostRecentActivity ?? getGroupMostRecentActivity(activities);
- if (groupMostRecentActivity?.type === 'reprocess') {
- if (groupCount === 0) {
- return ReprocessingStatus.REPROCESSED_AND_HASNT_EVENT;
- }
- return ReprocessingStatus.REPROCESSED_AND_HAS_EVENT;
- }
- return ReprocessingStatus.NO_STATUS;
- }
- default:
- return ReprocessingStatus.NO_STATUS;
- }
- }
- export const useFetchIssueTagsForDetailsPage = (
- {
- groupId,
- orgSlug,
- environment = [],
- isStatisticalDetector = false,
- statisticalDetectorParameters,
- }: {
- environment: string[];
- orgSlug: string;
- groupId?: string;
- isStatisticalDetector?: boolean;
- statisticalDetectorParameters?: {
- durationBaseline: number;
- end: string;
- start: string;
- transaction: string;
- };
- },
- {enabled = true}: {enabled?: boolean} = {}
- ) => {
- return useFetchIssueTags(
- {
- groupId,
- orgSlug,
- environment,
- readable: true,
- limit: 4,
- isStatisticalDetector,
- statisticalDetectorParameters,
- },
- {enabled}
- );
- };
- export function useEnvironmentsFromUrl(): string[] {
- const location = useLocation();
- const envs = location.query.environment;
- const envsArray = useMemo(() => {
- return typeof envs === 'string' ? [envs] : envs ?? [];
- }, [envs]);
- return envsArray;
- }
- export function getGroupDetailsQueryData({
- environments,
- }: {
- environments?: string[];
- } = {}): Record<string, string | string[]> {
- // Note, we do not want to include the environment key at all if there are no environments
- const query: Record<string, string | string[]> = {
- ...(environments && environments.length > 0 ? {environment: environments} : {}),
- expand: ['inbox', 'owners'],
- collapse: ['release', 'tags'],
- };
- return query;
- }
- export function getGroupEventDetailsQueryData({
- environments,
- query,
- stacktraceOnly,
- }: {
- environments?: string[];
- query?: string;
- stacktraceOnly?: boolean;
- } = {}): Record<string, string | string[]> {
- const defaultParams = {
- collapse: stacktraceOnly ? ['stacktraceOnly'] : ['fullRelease'],
- ...(query ? {query} : {}),
- };
- if (!environments || environments.length === 0) {
- return defaultParams;
- }
- return {...defaultParams, environment: environments};
- }
- export function useHasStreamlinedUI() {
- const location = useLocation();
- const organization = useOrganization();
- if (location.query.streamline === '0') {
- return false;
- }
- return (
- location.query.streamline === '1' ||
- organization.features.includes('issue-details-streamline')
- );
- }
|