@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.
- package/Server/Services/Index.ts +2 -0
- package/Server/Utils/StartServer.ts +25 -12
- package/Tests/UI/Components/ComponentsModal.test.tsx +88 -38
- package/UI/Components/Workflow/ComponentsModal.tsx +348 -59
- package/UI/Utils/Navigation.ts +1 -0
- package/UI/esbuild-config.js +29 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +17 -11
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Tests/UI/Components/ComponentsModal.test.js +33 -24
- package/build/dist/Tests/UI/Components/ComponentsModal.test.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentsModal.js +194 -46
- package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +1 -0
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/package.json +1 -1
package/Server/Services/Index.ts
CHANGED
|
@@ -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
|
|
121
|
-
*
|
|
122
|
-
*
|
|
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
|
-
|
|
131
|
+
if (req.path.includes("/otlp/v1/")) {
|
|
132
|
+
return next();
|
|
133
|
+
}
|
|
131
134
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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 (
|
|
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(
|
|
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.
|
|
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(
|
|
191
|
-
|
|
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(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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(
|
|
277
|
-
|
|
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(
|
|
304
|
-
|
|
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
|
|
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
|
-
|
|
377
|
+
localComponents.push(newComponent);
|
|
350
378
|
const componentsWithCommonWord: ComponentMetadata[] =
|
|
351
|
-
|
|
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={
|
|
388
|
+
components={localComponents}
|
|
361
389
|
categories={mockedCategories}
|
|
362
390
|
/>,
|
|
363
391
|
);
|
|
364
392
|
|
|
365
|
-
fireEvent.change(
|
|
366
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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={
|
|
426
|
+
components={localComponents}
|
|
388
427
|
categories={mockedCategories}
|
|
389
428
|
/>,
|
|
390
429
|
);
|
|
391
430
|
|
|
392
|
-
fireEvent.change(
|
|
393
|
-
|
|
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
|
|
403
|
-
|
|
404
|
-
|
|
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
|
-
|
|
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={
|
|
460
|
+
components={localComponents}
|
|
416
461
|
categories={mockedCategories}
|
|
417
462
|
/>,
|
|
418
463
|
);
|
|
419
464
|
|
|
420
|
-
fireEvent.change(
|
|
421
|
-
|
|
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
|
});
|