@oneuptime/common 10.0.45 → 10.0.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,6 +33,7 @@ import IncidentStateService from "./IncidentStateService";
33
33
  import IncidentStateTimelineService from "./IncidentStateTimelineService";
34
34
  //Labels.
35
35
  import LabelService from "./LabelService";
36
+ import KubernetesClusterService from "./KubernetesClusterService";
36
37
  import LlmProviderService from "./LlmProviderService";
37
38
  import LogService from "./LogService";
38
39
  import MailService from "./MailService";
@@ -256,6 +257,7 @@ const services: Array<BaseService> = [
256
257
  IncidentFeedService,
257
258
 
258
259
  LabelService,
260
+ KubernetesClusterService,
259
261
  LlmProviderService,
260
262
 
261
263
  MailService,
@@ -19,6 +19,7 @@ import Express, {
19
19
  NextFunction,
20
20
  OneUptimeRequest,
21
21
  RequestHandler,
22
+ headerValueToString,
22
23
  } from "./Express";
23
24
  import logger from "./Logger";
24
25
  import "./Process";
@@ -117,9 +118,9 @@ app.use((req: ExpressRequest, _res: ExpressResponse, next: NextFunction) => {
117
118
  });
118
119
 
119
120
  /*
120
- * Parse protobuf (binary) bodies for OTLP ingestion before JSON/gzip middleware.
121
- * The .NET OpenTelemetry SDK (and others) send telemetry data as application/x-protobuf.
122
- * Without this, express.json() skips protobuf requests and req.body remains undefined.
121
+ * Parse protobuf (binary) bodies for non-OTLP routes.
122
+ * OTLP HTTP ingestion bypasses the global body parsers and handles raw/gzip
123
+ * payloads in the telemetry router to avoid conflicts with the merged app stack.
123
124
  */
124
125
  const protobufBodyParserMiddleware: RequestHandler = ExpressRaw({
125
126
  type: ["application/x-protobuf", "application/protobuf"],
@@ -127,15 +128,18 @@ const protobufBodyParserMiddleware: RequestHandler = ExpressRaw({
127
128
  });
128
129
 
129
130
  app.use((req: OneUptimeRequest, res: ExpressResponse, next: NextFunction) => {
130
- const contentType: string | undefined = req.headers["content-type"];
131
+ if (req.path.includes("/otlp/v1/")) {
132
+ return next();
133
+ }
131
134
 
132
- if (
133
- contentType &&
134
- (contentType.includes("application/x-protobuf") ||
135
- contentType.includes("application/protobuf"))
136
- ) {
137
- protobufBodyParserMiddleware(req, res, next);
138
- } else if (req.headers["content-encoding"] === "gzip") {
135
+ const contentType: string | undefined = headerValueToString(
136
+ req.headers["content-type"],
137
+ );
138
+ const contentEncoding: string | undefined = headerValueToString(
139
+ req.headers["content-encoding"],
140
+ );
141
+
142
+ if (contentEncoding?.includes("gzip")) {
139
143
  const buffers: any = [];
140
144
 
141
145
  req.on("data", (chunk: any) => {
@@ -159,13 +163,22 @@ app.use((req: OneUptimeRequest, res: ExpressResponse, next: NextFunction) => {
159
163
  next();
160
164
  });
161
165
  });
166
+ } else if (
167
+ contentType &&
168
+ (contentType.includes("application/x-protobuf") ||
169
+ contentType.includes("application/protobuf"))
170
+ ) {
171
+ protobufBodyParserMiddleware(req, res, next);
162
172
  } else {
163
173
  jsonBodyParserMiddleware(req, res, next);
164
174
  }
165
175
  });
166
176
 
167
177
  app.use((req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
168
- if (req.headers["content-encoding"] === "gzip") {
178
+ if (
179
+ req.path.includes("/otlp/v1/") ||
180
+ headerValueToString(req.headers["content-encoding"])?.includes("gzip")
181
+ ) {
169
182
  next();
170
183
  } else {
171
184
  urlEncodedMiddleware(req, res, next);
@@ -86,7 +86,9 @@ describe("ComponentsModal", () => {
86
86
  />,
87
87
  );
88
88
  expect(
89
- screen.getByPlaceholderText("Search components..."),
89
+ screen.getByPlaceholderText(
90
+ "Search components by name, description, or category",
91
+ ),
90
92
  ).toBeInTheDocument();
91
93
  });
92
94
 
@@ -101,7 +103,7 @@ describe("ComponentsModal", () => {
101
103
  />,
102
104
  );
103
105
  for (const cat of mockedCategories) {
104
- expect(screen.getByText(cat.name)).toBeInTheDocument();
106
+ expect(screen.getAllByText(cat.name).length).toBeGreaterThanOrEqual(1);
105
107
  }
106
108
  for (const comp of mockedComponents) {
107
109
  expect(screen.getByText(comp.title)).toBeInTheDocument();
@@ -187,9 +189,14 @@ describe("ComponentsModal", () => {
187
189
  categories={mockedCategories}
188
190
  />,
189
191
  );
190
- fireEvent.change(screen.getByPlaceholderText("Search components..."), {
191
- target: { value: "Non-existent Ccmponent" },
192
- });
192
+ fireEvent.change(
193
+ screen.getByPlaceholderText(
194
+ "Search components by name, description, or category",
195
+ ),
196
+ {
197
+ target: { value: "Non-existent Ccmponent" },
198
+ },
199
+ );
193
200
  expect(
194
201
  screen.getByText(
195
202
  "No components that match your search. If you are looking for an integration that does not exist currently - you can use Custom Code or API component to build anything you like.",
@@ -246,10 +253,20 @@ describe("ComponentsModal", () => {
246
253
  0,
247
254
  comp.title.length - comp.title.length / 2,
248
255
  );
249
- fireEvent.change(screen.getByPlaceholderText("Search components..."), {
250
- target: { value: partialTitle },
251
- });
252
- expect(screen.getByText(comp.title)).toBeInTheDocument();
256
+ fireEvent.change(
257
+ screen.getByPlaceholderText(
258
+ "Search components by name, description, or category",
259
+ ),
260
+ {
261
+ target: { value: partialTitle },
262
+ },
263
+ );
264
+ // title may be split across elements due to search highlighting
265
+ expect(
266
+ screen.getByText((_content: string | null, element: Element | null) => {
267
+ return element?.textContent === comp.title;
268
+ }),
269
+ ).toBeInTheDocument();
253
270
 
254
271
  // check other components are not displayed
255
272
  mockedComponents
@@ -273,9 +290,14 @@ describe("ComponentsModal", () => {
273
290
  />,
274
291
  );
275
292
  mockedComponents.forEach((comp: ComponentMetadata) => {
276
- fireEvent.change(screen.getByPlaceholderText("Search components..."), {
277
- target: { value: comp.description },
278
- });
293
+ fireEvent.change(
294
+ screen.getByPlaceholderText(
295
+ "Search components by name, description, or category",
296
+ ),
297
+ {
298
+ target: { value: comp.description },
299
+ },
300
+ );
279
301
  expect(screen.getByText(comp.title)).toBeInTheDocument();
280
302
 
281
303
  // check other components are not displayed
@@ -300,9 +322,14 @@ describe("ComponentsModal", () => {
300
322
  />,
301
323
  );
302
324
  mockedComponents.forEach((comp: ComponentMetadata) => {
303
- fireEvent.change(screen.getByPlaceholderText("Search components..."), {
304
- target: { value: comp.category },
305
- });
325
+ fireEvent.change(
326
+ screen.getByPlaceholderText(
327
+ "Search components by name, description, or category",
328
+ ),
329
+ {
330
+ target: { value: comp.category },
331
+ },
332
+ );
306
333
  expect(screen.getByText(comp.title)).toBeInTheDocument();
307
334
 
308
335
  // check other components are not displayed
@@ -328,7 +355,7 @@ describe("ComponentsModal", () => {
328
355
  );
329
356
  mockedComponents.forEach((comp: ComponentMetadata) => {
330
357
  const searchInput: HTMLElement = screen.getByPlaceholderText(
331
- "Search components...",
358
+ "Search components by name, description, or category",
332
359
  );
333
360
  fireEvent.change(searchInput, { target: { value: comp.title } });
334
361
  fireEvent.change(searchInput, { target: { value: "" } }); // clear search
@@ -341,14 +368,15 @@ describe("ComponentsModal", () => {
341
368
 
342
369
  it("should return multiple components when similar titles match", () => {
343
370
  // we add a new component where its title is a substring of another component's title
344
- const commonWord: string = mockedComponents[0]?.title.substring(0, 5) || "";
371
+ const localComponents: ComponentMetadata[] = [...mockedComponents];
372
+ const commonWord: string = localComponents[0]?.title.substring(0, 5) || "";
345
373
  const newComponent: ComponentMetadata = getComponentMetadata(
346
374
  mockedCategories[1]?.name,
347
375
  );
348
376
  newComponent.title += commonWord;
349
- mockedComponents.push(newComponent);
377
+ localComponents.push(newComponent);
350
378
  const componentsWithCommonWord: ComponentMetadata[] =
351
- mockedComponents.filter((comp: ComponentMetadata) => {
379
+ localComponents.filter((comp: ComponentMetadata) => {
352
380
  return comp.title.includes(commonWord);
353
381
  });
354
382
 
@@ -357,41 +385,57 @@ describe("ComponentsModal", () => {
357
385
  componentsType={ComponentType.Component}
358
386
  onCloseModal={mockOnCloseModal}
359
387
  onComponentClick={mockOnComponentClick}
360
- components={mockedComponents}
388
+ components={localComponents}
361
389
  categories={mockedCategories}
362
390
  />,
363
391
  );
364
392
 
365
- fireEvent.change(screen.getByPlaceholderText("Search components..."), {
366
- target: { value: commonWord },
367
- });
393
+ fireEvent.change(
394
+ screen.getByPlaceholderText(
395
+ "Search components by name, description, or category",
396
+ ),
397
+ {
398
+ target: { value: commonWord },
399
+ },
400
+ );
368
401
  componentsWithCommonWord.forEach((comp: ComponentMetadata) => {
369
- expect(screen.getByText(comp.title)).toBeInTheDocument();
402
+ // title may be split across elements due to search highlighting
403
+ expect(
404
+ screen.getByText((_content: string | null, element: Element | null) => {
405
+ return element?.textContent === comp.title;
406
+ }),
407
+ ).toBeInTheDocument();
370
408
  });
371
409
  });
372
410
 
373
411
  it("should return return components with similar descriptions", () => {
374
412
  // we add a new component where its title is a substring of another component's description
413
+ const localComponents: ComponentMetadata[] = [...mockedComponents];
375
414
  const partialDescription: string =
376
- mockedComponents[0]?.description.substring(0, 10) || "";
415
+ localComponents[0]?.description.substring(0, 10) || "";
377
416
  const newComponent: ComponentMetadata = getComponentMetadata(
378
417
  mockedCategories[1]?.name,
379
418
  );
380
419
  newComponent.title = partialDescription || "";
381
- mockedComponents.push(newComponent);
420
+ localComponents.push(newComponent);
382
421
  render(
383
422
  <ComponentsModal
384
423
  componentsType={ComponentType.Component}
385
424
  onCloseModal={mockOnCloseModal}
386
425
  onComponentClick={mockOnComponentClick}
387
- components={mockedComponents}
426
+ components={localComponents}
388
427
  categories={mockedCategories}
389
428
  />,
390
429
  );
391
430
 
392
- fireEvent.change(screen.getByPlaceholderText("Search components..."), {
393
- target: { value: partialDescription },
394
- });
431
+ fireEvent.change(
432
+ screen.getByPlaceholderText(
433
+ "Search components by name, description, or category",
434
+ ),
435
+ {
436
+ target: { value: partialDescription },
437
+ },
438
+ );
395
439
  expect(
396
440
  screen.getAllByText(new RegExp(partialDescription, "i")),
397
441
  ).toHaveLength(2);
@@ -399,11 +443,12 @@ describe("ComponentsModal", () => {
399
443
 
400
444
  it("should return components with the same category", () => {
401
445
  // we add two components with the same category as the first component
402
- const commonCategory: string | undefined = mockedComponents[0]?.category;
403
- mockedComponents.push(getComponentMetadata(commonCategory));
404
- mockedComponents.push(getComponentMetadata(commonCategory));
446
+ const localComponents: ComponentMetadata[] = [...mockedComponents];
447
+ const commonCategory: string | undefined = localComponents[0]?.category;
448
+ localComponents.push(getComponentMetadata(commonCategory));
449
+ localComponents.push(getComponentMetadata(commonCategory));
405
450
  const componentsInCommonCategory: ComponentMetadata[] =
406
- mockedComponents.filter((comp: ComponentMetadata) => {
451
+ localComponents.filter((comp: ComponentMetadata) => {
407
452
  return comp.category === commonCategory;
408
453
  });
409
454
 
@@ -412,14 +457,19 @@ describe("ComponentsModal", () => {
412
457
  componentsType={ComponentType.Component}
413
458
  onCloseModal={mockOnCloseModal}
414
459
  onComponentClick={mockOnComponentClick}
415
- components={mockedComponents}
460
+ components={localComponents}
416
461
  categories={mockedCategories}
417
462
  />,
418
463
  );
419
464
 
420
- fireEvent.change(screen.getByPlaceholderText("Search components..."), {
421
- target: { value: commonCategory },
422
- });
465
+ fireEvent.change(
466
+ screen.getByPlaceholderText(
467
+ "Search components by name, description, or category",
468
+ ),
469
+ {
470
+ target: { value: commonCategory },
471
+ },
472
+ );
423
473
  componentsInCommonCategory.forEach((comp: ComponentMetadata) => {
424
474
  expect(screen.getByText(comp.title)).toBeInTheDocument();
425
475
  });