utils.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import {RawSpanType} from 'sentry/components/events/interfaces/spans/types';
  2. import {EntryType, EventOrGroupType, EventTransaction} from 'sentry/types';
  3. export enum ProblemSpan {
  4. PARENT = 'parent',
  5. OFFENDER = 'offender',
  6. }
  7. export const EXAMPLE_TRANSACTION_TITLE = '/api/0/transaction-test-endpoint/';
  8. type AddSpanOpts = {
  9. endTimestamp: number;
  10. startTimestamp: number;
  11. description?: string;
  12. op?: string;
  13. problemSpan?: ProblemSpan;
  14. status?: string;
  15. };
  16. export class TransactionEventBuilder {
  17. TRACE_ID = '8cbbc19c0f54447ab702f00263262726';
  18. ROOT_SPAN_ID = '0000000000000000';
  19. #event: EventTransaction;
  20. #spans: RawSpanType[] = [];
  21. constructor(id?: string, title?: string) {
  22. this.#event = {
  23. id: id ?? 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
  24. eventID: id ?? 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
  25. title: title ?? EXAMPLE_TRANSACTION_TITLE,
  26. type: EventOrGroupType.TRANSACTION,
  27. startTimestamp: 0,
  28. endTimestamp: 0,
  29. contexts: {
  30. trace: {
  31. trace_id: this.TRACE_ID,
  32. span_id: this.ROOT_SPAN_ID,
  33. op: 'pageload',
  34. status: 'ok',
  35. type: 'trace',
  36. },
  37. },
  38. entries: [
  39. {
  40. data: this.#spans,
  41. type: EntryType.SPANS,
  42. },
  43. ],
  44. perfProblem: {
  45. causeSpanIds: [],
  46. offenderSpanIds: [],
  47. parentSpanIds: [],
  48. },
  49. // For the purpose of mock data, we don't care as much about the properties below.
  50. // They're here to satisfy the type constraints, but in the future if we need actual values here
  51. // for testing purposes, we can add methods on the builder to set them.
  52. crashFile: null,
  53. culprit: '',
  54. dateReceived: '',
  55. dist: null,
  56. errors: [],
  57. fingerprints: [],
  58. location: null,
  59. message: '',
  60. metadata: {
  61. current_level: undefined,
  62. current_tree_label: undefined,
  63. directive: undefined,
  64. display_title_with_tree_label: undefined,
  65. filename: undefined,
  66. finest_tree_label: undefined,
  67. function: undefined,
  68. message: undefined,
  69. origin: undefined,
  70. stripped_crash: undefined,
  71. title: undefined,
  72. type: undefined,
  73. uri: undefined,
  74. value: undefined,
  75. },
  76. projectID: '',
  77. size: 0,
  78. tags: [],
  79. user: null,
  80. };
  81. }
  82. generateSpanId() {
  83. // Convert the num of spans to a hex string to get its ID
  84. return (this.#spans.length + 1).toString(16).padStart(16, '0');
  85. }
  86. addSpan(mockSpan: MockSpan, numSpans = 1, parentSpanId?: string) {
  87. for (let i = 0; i < numSpans; i++) {
  88. const spanId = this.generateSpanId();
  89. const {span} = mockSpan;
  90. const clonedSpan = {...span};
  91. clonedSpan.span_id = spanId;
  92. clonedSpan.trace_id = this.TRACE_ID;
  93. clonedSpan.parent_span_id = parentSpanId ?? this.ROOT_SPAN_ID;
  94. this.#spans.push(clonedSpan);
  95. switch (mockSpan.problemSpan) {
  96. case ProblemSpan.PARENT:
  97. this.#event.perfProblem?.parentSpanIds.push(spanId);
  98. break;
  99. case ProblemSpan.OFFENDER:
  100. this.#event.perfProblem?.offenderSpanIds.push(spanId);
  101. break;
  102. default:
  103. break;
  104. }
  105. if (clonedSpan.timestamp > this.#event.endTimestamp) {
  106. this.#event.endTimestamp = clonedSpan.timestamp;
  107. }
  108. mockSpan.children.forEach(child => this.addSpan(child, 1, spanId));
  109. }
  110. return this;
  111. }
  112. getEvent() {
  113. return this.#event;
  114. }
  115. }
  116. /**
  117. * A MockSpan object to be used for testing. This object is intended to be used in tandem with `TransactionEventBuilder`
  118. */
  119. export class MockSpan {
  120. span: RawSpanType;
  121. children: MockSpan[] = [];
  122. problemSpan: ProblemSpan | undefined;
  123. /**
  124. *
  125. * @param opts.startTimestamp
  126. * @param opts.endTimestamp
  127. * @param opts.op The operation of the span
  128. * @param opts.description The description of the span
  129. * @param opts.status Optional span specific status, defaults to 'ok'
  130. * @param opts.problemSpan If this span should be part of a performance problem, indicates the type of problem span (i.e ProblemSpan.OFFENDER, ProblemSpan.PARENT)
  131. * @param opts.parentSpanId When provided, will explicitly set this span's parent ID. If you are creating nested spans via `addChild` on the `MockSpan` object,
  132. * this will be handled automatically and you do not need to provide an ID. Defaults to the root span's ID.
  133. */
  134. constructor(opts: AddSpanOpts) {
  135. const {startTimestamp, endTimestamp, op, description, status, problemSpan} = opts;
  136. this.span = {
  137. start_timestamp: startTimestamp,
  138. timestamp: endTimestamp,
  139. op,
  140. description,
  141. status: status ?? 'ok',
  142. data: {},
  143. // These values are automatically assigned by the TransactionEventBuilder when the spans are added
  144. span_id: '',
  145. trace_id: '',
  146. parent_span_id: '',
  147. };
  148. this.problemSpan = problemSpan;
  149. }
  150. /**
  151. *
  152. * @param opts.numSpans If provided, will create the same span numSpan times
  153. */
  154. addChild(opts: AddSpanOpts, numSpans = 1) {
  155. const {startTimestamp, endTimestamp, op, description, status, problemSpan} = opts;
  156. for (let i = 0; i < numSpans; i++) {
  157. const span = new MockSpan({
  158. startTimestamp,
  159. endTimestamp,
  160. op,
  161. description,
  162. status,
  163. problemSpan,
  164. });
  165. this.children.push(span);
  166. }
  167. return this;
  168. }
  169. /**
  170. * Allows you to create a nested group of duplicate mock spans by duplicating the current span. This is useful for simulating the nested 'autogrouped' condition on the span tree.
  171. * Will create `depth` spans, each span being a child of the previous.
  172. * @param depth
  173. */
  174. addDuplicateNestedChildren(depth = 1) {
  175. let currentSpan: MockSpan = this;
  176. for (let i = 0; i < depth; i++) {
  177. currentSpan.addChild(currentSpan.getOpts());
  178. currentSpan = currentSpan.children[0];
  179. }
  180. return this;
  181. }
  182. getOpts() {
  183. return {
  184. startTimestamp: this.span.start_timestamp,
  185. endTimestamp: this.span.timestamp,
  186. op: this.span.op,
  187. description: this.span.description,
  188. status: this.span.status,
  189. problemSpan: this.problemSpan,
  190. };
  191. }
  192. }