123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- import * as Sentry from '@sentry/react';
- import type {RequestCallbacks, RequestOptions} from 'sentry/api';
- import {Client} from 'sentry/api';
- import GroupStore from 'sentry/stores/groupStore';
- import type {Actor} from 'sentry/types/core';
- import type {Group, Tag as GroupTag, TagValue} from 'sentry/types/group';
- import {buildTeamId, buildUserId} from 'sentry/utils';
- import {uniqueId} from 'sentry/utils/guid';
- import type {ApiQueryKey, UseApiQueryOptions} from 'sentry/utils/queryClient';
- import {useApiQuery} from 'sentry/utils/queryClient';
- type AssignedBy = 'suggested_assignee' | 'assignee_selector';
- export function clearAssignment(
- groupId: string,
- orgSlug: string,
- assignedBy: AssignedBy
- ): Promise<Group> {
- const api = new Client();
- const endpoint = `/organizations/${orgSlug}/issues/${groupId}/`;
- const id = uniqueId();
- GroupStore.onAssignTo(id, groupId, {
- email: '',
- });
- const request = api.requestPromise(endpoint, {
- method: 'PUT',
- // Sending an empty value to assignedTo is the same as "clear"
- data: {
- assignedTo: '',
- assignedBy,
- },
- });
- request
- .then(data => {
- GroupStore.onAssignToSuccess(id, groupId, data);
- return data;
- })
- .catch(data => {
- GroupStore.onAssignToError(id, groupId, data);
- throw data;
- });
- return request;
- }
- type AssignToActorParams = {
- actor: Pick<Actor, 'id' | 'type'>;
- assignedBy: AssignedBy;
- /**
- * Issue id
- */
- id: string;
- orgSlug: string;
- };
- export function assignToActor({
- id,
- actor,
- assignedBy,
- orgSlug,
- }: AssignToActorParams): Promise<Group> {
- const api = new Client();
- const endpoint = `/organizations/${orgSlug}/issues/${id}/`;
- const guid = uniqueId();
- let actorId = '';
- GroupStore.onAssignTo(guid, id, {email: ''});
- switch (actor.type) {
- case 'user':
- actorId = buildUserId(actor.id);
- break;
- case 'team':
- actorId = buildTeamId(actor.id);
- break;
- default:
- Sentry.withScope(scope => {
- scope.setExtra('actor', actor);
- Sentry.captureException('Unknown assignee type');
- });
- }
- return api
- .requestPromise(endpoint, {
- method: 'PUT',
- data: {assignedTo: actorId, assignedBy},
- })
- .then(data => {
- GroupStore.onAssignToSuccess(guid, id, data);
- return data;
- })
- .catch(data => {
- GroupStore.onAssignToSuccess(guid, id, data);
- throw data;
- });
- }
- type ParamsType = {
- environment?: string | string[] | null;
- itemIds?: string[];
- project?: number[] | string[] | null;
- query?: string;
- };
- type UpdateParams = ParamsType & {
- orgId: string;
- projectId?: string;
- };
- type QueryArgs =
- | {
- query: string;
- environment?: string | string[];
- project?: Array<number | string>;
- }
- | {
- id: number[] | string[];
- environment?: string | string[];
- project?: Array<number | string>;
- }
- | {
- environment?: string | string[];
- project?: Array<number | string>;
- };
- /**
- * Converts input parameters to API-compatible query arguments
- */
- export function paramsToQueryArgs(params: ParamsType): QueryArgs {
- const p: QueryArgs = params.itemIds
- ? {id: params.itemIds} // items matching array of itemids
- : params.query
- ? {query: params.query} // items matching search query
- : {}; // all items
- // only include environment if it is not null/undefined
- if (params.query && params.environment !== null && params.environment !== undefined) {
- p.environment = params.environment;
- }
- // only include projects if it is not null/undefined/an empty array
- if (params.project?.length) {
- p.project = params.project;
- }
- // only include date filters if they are not null/undefined
- if (params.query) {
- ['start', 'end', 'period', 'utc'].forEach(prop => {
- if (
- params[prop as keyof typeof params] !== null &&
- params[prop as keyof typeof params] !== undefined
- ) {
- (p as any)[prop === 'period' ? 'statsPeriod' : prop] =
- params[prop as keyof typeof params];
- }
- });
- }
- return p;
- }
- function getUpdateUrl({projectId, orgId}: UpdateParams) {
- return projectId
- ? `/projects/${orgId}/${projectId}/issues/`
- : `/organizations/${orgId}/issues/`;
- }
- function chainUtil<Args extends any[]>(
- ...funcs: Array<((...args: Args) => any) | undefined>
- ) {
- const filteredFuncs = funcs.filter(
- (f): f is (...args: Args) => any => typeof f === 'function'
- );
- return (...args: Args): void => {
- filteredFuncs.forEach(func => {
- func.apply(funcs, args);
- });
- };
- }
- function wrapRequest(
- api: Client,
- path: string,
- options: RequestOptions,
- extraParams: RequestCallbacks = {}
- ) {
- options.success = chainUtil(options.success, extraParams.success);
- options.error = chainUtil(options.error, extraParams.error);
- options.complete = chainUtil(options.complete, extraParams.complete);
- return api.request(path, options);
- }
- type BulkDeleteParams = UpdateParams;
- export function bulkDelete(
- api: Client,
- params: BulkDeleteParams,
- options: RequestCallbacks
- ) {
- const {itemIds} = params;
- const path = getUpdateUrl(params);
- const query: QueryArgs = paramsToQueryArgs(params);
- const id = uniqueId();
- GroupStore.onDelete(id, itemIds);
- return wrapRequest(
- api,
- path,
- {
- query,
- method: 'DELETE',
- success: response => {
- GroupStore.onDeleteSuccess(id, itemIds, response);
- },
- error: error => {
- GroupStore.onDeleteError(id, itemIds, error);
- },
- },
- options
- );
- }
- type BulkUpdateParams = UpdateParams & {
- data?: any;
- failSilently?: boolean;
- };
- export function bulkUpdate(
- api: Client,
- params: BulkUpdateParams,
- options: RequestCallbacks
- ) {
- const {itemIds, failSilently, data} = params;
- const path = getUpdateUrl(params);
- const query: QueryArgs = paramsToQueryArgs(params);
- const id = uniqueId();
- GroupStore.onUpdate(id, itemIds, data);
- return wrapRequest(
- api,
- path,
- {
- query,
- method: 'PUT',
- data,
- success: response => {
- GroupStore.onUpdateSuccess(id, itemIds, response);
- },
- error: () => {
- GroupStore.onUpdateError(id, itemIds, !!failSilently);
- },
- },
- options
- );
- }
- type MergeGroupsParams = UpdateParams;
- export function mergeGroups(
- api: Client,
- params: MergeGroupsParams,
- options: RequestCallbacks
- ) {
- const {itemIds} = params;
- const path = getUpdateUrl(params);
- const query: QueryArgs = paramsToQueryArgs(params);
- const id = uniqueId();
- GroupStore.onMerge(id, itemIds);
- return wrapRequest(
- api,
- path,
- {
- query,
- method: 'PUT',
- data: {merge: 1},
- success: response => {
- GroupStore.onMergeSuccess(id, itemIds, response);
- },
- error: error => {
- GroupStore.onMergeError(id, itemIds, error);
- },
- },
- options
- );
- }
- type FetchIssueTagValuesParameters = {
- groupId: string;
- orgSlug: string;
- tagKey: string;
- cursor?: string;
- environment?: string[];
- sort?: string | string[];
- };
- export const makeFetchIssueTagValuesQueryKey = ({
- orgSlug,
- groupId,
- tagKey,
- environment,
- sort,
- cursor,
- }: FetchIssueTagValuesParameters): ApiQueryKey => [
- `/organizations/${orgSlug}/issues/${groupId}/tags/${tagKey}/values/`,
- {query: {environment, sort, cursor}},
- ];
- export function useFetchIssueTagValues(
- parameters: FetchIssueTagValuesParameters,
- options: Partial<UseApiQueryOptions<TagValue[]>> = {}
- ) {
- return useApiQuery<TagValue[]>(makeFetchIssueTagValuesQueryKey(parameters), {
- staleTime: 0,
- retry: false,
- ...options,
- });
- }
- type FetchIssueTagParameters = {
- groupId: string;
- orgSlug: string;
- tagKey: string;
- };
- export const makeFetchIssueTagQueryKey = ({
- orgSlug,
- groupId,
- tagKey,
- environment,
- sort,
- }: FetchIssueTagValuesParameters): ApiQueryKey => [
- `/organizations/${orgSlug}/issues/${groupId}/tags/${tagKey}/`,
- {query: {environment, sort}},
- ];
- export function useFetchIssueTag(
- parameters: FetchIssueTagParameters,
- options: Partial<UseApiQueryOptions<GroupTag>> = {}
- ) {
- return useApiQuery<GroupTag>(makeFetchIssueTagQueryKey(parameters), {
- staleTime: 0,
- retry: false,
- ...options,
- });
- }
|