index.tsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import type React from 'react';
  2. import {createContext, useCallback, useContext, useMemo} from 'react';
  3. import type {Location} from 'history';
  4. import {defined} from 'sentry/utils';
  5. import type {Sort} from 'sentry/utils/discover/fields';
  6. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  7. import {useLocation} from 'sentry/utils/useLocation';
  8. import {useNavigate} from 'sentry/utils/useNavigate';
  9. import useOrganization from 'sentry/utils/useOrganization';
  10. import {
  11. defaultDataset,
  12. getDatasetFromLocation,
  13. updateLocationWithDataset,
  14. } from './dataset';
  15. import {defaultFields, getFieldsFromLocation, updateLocationWithFields} from './fields';
  16. import {
  17. defaultGroupBys,
  18. getGroupBysFromLocation,
  19. updateLocationWithGroupBys,
  20. } from './groupBys';
  21. import {defaultMode, getModeFromLocation, Mode, updateLocationWithMode} from './mode';
  22. import {defaultQuery, getQueryFromLocation, updateLocationWithQuery} from './query';
  23. import {
  24. defaultSortBys,
  25. getSortBysFromLocation,
  26. updateLocationWithSortBys,
  27. } from './sortBys';
  28. import {defaultTitle, getTitleFromLocation, updateLocationWithTitle} from './title';
  29. import type {BaseVisualize, Visualize} from './visualizes';
  30. import {
  31. defaultVisualizes,
  32. getVisualizesFromLocation,
  33. updateLocationWithVisualizes,
  34. } from './visualizes';
  35. interface ReadablePageParams {
  36. dataset: DiscoverDatasets | undefined;
  37. fields: string[];
  38. groupBys: string[];
  39. mode: Mode;
  40. query: string;
  41. sortBys: Sort[];
  42. visualizes: Visualize[];
  43. title?: string;
  44. }
  45. interface WritablePageParams {
  46. dataset?: DiscoverDatasets | null;
  47. fields?: string[] | null;
  48. groupBys?: string[] | null;
  49. mode?: Mode | null;
  50. query?: string | null;
  51. sortBys?: Sort[] | null;
  52. title?: string | null;
  53. visualizes?: BaseVisualize[] | null;
  54. }
  55. export interface SuggestedQuery {
  56. fields: string[];
  57. groupBys: string[];
  58. mode: Mode;
  59. query: string;
  60. sortBys: Sort[];
  61. title: string;
  62. visualizes: BaseVisualize[];
  63. }
  64. function defaultPageParams(): ReadablePageParams {
  65. const dataset = defaultDataset();
  66. const fields = defaultFields();
  67. const groupBys = defaultGroupBys();
  68. const mode = defaultMode();
  69. const query = defaultQuery();
  70. const visualizes = defaultVisualizes();
  71. const title = defaultTitle();
  72. const sortBys = defaultSortBys(
  73. mode,
  74. fields,
  75. visualizes.flatMap(visualize => visualize.yAxes)
  76. );
  77. return {
  78. dataset,
  79. fields,
  80. groupBys,
  81. mode,
  82. query,
  83. sortBys,
  84. title,
  85. visualizes,
  86. };
  87. }
  88. const PageParamsContext = createContext<ReadablePageParams>(defaultPageParams());
  89. interface PageParamsProviderProps {
  90. children: React.ReactNode;
  91. }
  92. export function PageParamsProvider({children}: PageParamsProviderProps) {
  93. const location = useLocation();
  94. const organization = useOrganization();
  95. const pageParams: ReadablePageParams = useMemo(() => {
  96. const dataset = getDatasetFromLocation(location);
  97. const fields = getFieldsFromLocation(location);
  98. const groupBys = getGroupBysFromLocation(location);
  99. const mode = getModeFromLocation(location);
  100. const query = getQueryFromLocation(location);
  101. const visualizes = getVisualizesFromLocation(location, organization);
  102. const sortBys = getSortBysFromLocation(location, mode, fields, groupBys, visualizes);
  103. const title = getTitleFromLocation(location);
  104. return {
  105. dataset,
  106. fields,
  107. groupBys,
  108. mode,
  109. query,
  110. sortBys,
  111. title,
  112. visualizes,
  113. };
  114. }, [location, organization]);
  115. return (
  116. <PageParamsContext.Provider value={pageParams}>{children}</PageParamsContext.Provider>
  117. );
  118. }
  119. export function useExplorePageParams(): ReadablePageParams {
  120. return useContext(PageParamsContext);
  121. }
  122. export function useExploreDataset(): DiscoverDatasets {
  123. const organization = useOrganization();
  124. const pageParams = useExplorePageParams();
  125. if (defined(pageParams.dataset)) {
  126. return pageParams.dataset;
  127. }
  128. return organization.features.includes('visibility-explore-rpc')
  129. ? DiscoverDatasets.SPANS_EAP_RPC
  130. : DiscoverDatasets.SPANS_EAP;
  131. }
  132. export function useExploreFields(): string[] {
  133. const pageParams = useExplorePageParams();
  134. return pageParams.fields;
  135. }
  136. export function useExploreGroupBys(): string[] {
  137. const pageParams = useExplorePageParams();
  138. return pageParams.groupBys;
  139. }
  140. export function useExploreMode(): Mode {
  141. const pageParams = useExplorePageParams();
  142. return pageParams.mode;
  143. }
  144. export function useExploreQuery(): string {
  145. const pageParams = useExplorePageParams();
  146. return pageParams.query;
  147. }
  148. export function useExploreSortBys(): Sort[] {
  149. const pageParams = useExplorePageParams();
  150. return pageParams.sortBys;
  151. }
  152. export function useExploreTitle(): string | undefined {
  153. const pageParams = useExplorePageParams();
  154. return pageParams.title;
  155. }
  156. export function useExploreVisualizes(): Visualize[] {
  157. const pageParams = useExplorePageParams();
  158. return pageParams.visualizes;
  159. }
  160. export function newExploreTarget(
  161. location: Location,
  162. pageParams: WritablePageParams
  163. ): Location {
  164. const target = {...location, query: {...location.query}};
  165. updateLocationWithDataset(target, pageParams.dataset);
  166. updateLocationWithFields(target, pageParams.fields);
  167. updateLocationWithGroupBys(target, pageParams.groupBys);
  168. updateLocationWithMode(target, pageParams.mode);
  169. updateLocationWithQuery(target, pageParams.query);
  170. updateLocationWithSortBys(target, pageParams.sortBys);
  171. updateLocationWithVisualizes(target, pageParams.visualizes);
  172. updateLocationWithTitle(target, pageParams.title);
  173. return target;
  174. }
  175. export function useSetExplorePageParams() {
  176. const location = useLocation();
  177. const navigate = useNavigate();
  178. return useCallback(
  179. (pageParams: WritablePageParams) => {
  180. const target = newExploreTarget(location, pageParams);
  181. navigate(target);
  182. },
  183. [location, navigate]
  184. );
  185. }
  186. export function useSetExploreDataset() {
  187. const setPageParams = useSetExplorePageParams();
  188. return useCallback(
  189. (dataset: DiscoverDatasets) => {
  190. setPageParams({dataset});
  191. },
  192. [setPageParams]
  193. );
  194. }
  195. export function useSetExploreFields() {
  196. const setPageParams = useSetExplorePageParams();
  197. return useCallback(
  198. (fields: string[]) => {
  199. setPageParams({fields});
  200. },
  201. [setPageParams]
  202. );
  203. }
  204. export function useSetExploreGroupBys() {
  205. const setPageParams = useSetExplorePageParams();
  206. return useCallback(
  207. (groupBys: string[]) => {
  208. setPageParams({groupBys});
  209. },
  210. [setPageParams]
  211. );
  212. }
  213. export function useSetExploreMode() {
  214. const pageParams = useExplorePageParams();
  215. const setPageParams = useSetExplorePageParams();
  216. return useCallback(
  217. (mode: Mode) => {
  218. if (mode === Mode.SAMPLES && pageParams.groupBys.some(groupBy => groupBy !== '')) {
  219. // When switching from the aggregates to samples mode, carry
  220. // over any group bys as they are helpful context when looking
  221. // for examples.
  222. const fields = [...pageParams.fields];
  223. for (const groupBy of pageParams.groupBys) {
  224. if (groupBy !== '' && !fields.includes(groupBy)) {
  225. fields.push(groupBy);
  226. }
  227. }
  228. setPageParams({
  229. mode,
  230. fields,
  231. });
  232. } else {
  233. setPageParams({mode});
  234. }
  235. },
  236. [pageParams, setPageParams]
  237. );
  238. }
  239. export function useSetExploreQuery() {
  240. const setPageParams = useSetExplorePageParams();
  241. return useCallback(
  242. (query: string) => {
  243. setPageParams({query});
  244. },
  245. [setPageParams]
  246. );
  247. }
  248. export function useSetExploreSortBys() {
  249. const setPageParams = useSetExplorePageParams();
  250. return useCallback(
  251. (sortBys: Sort[]) => {
  252. setPageParams({sortBys});
  253. },
  254. [setPageParams]
  255. );
  256. }
  257. export function useSetExploreVisualizes() {
  258. const pageParams = useExplorePageParams();
  259. const setPageParams = useSetExplorePageParams();
  260. return useCallback(
  261. (visualizes: BaseVisualize[], fields?: string[]) => {
  262. const writablePageParams: WritablePageParams = {visualizes};
  263. const newFields = fields?.filter(field => !pageParams.fields.includes(field)) || [];
  264. if (newFields.length > 0) {
  265. writablePageParams.fields = [...pageParams.fields, ...newFields];
  266. }
  267. setPageParams(writablePageParams);
  268. },
  269. [pageParams, setPageParams]
  270. );
  271. }