@oneuptime/common 9.2.18 → 9.2.21
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/AIService.ts +1 -1
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +78 -1
- package/Server/Utils/Workspace/Slack/Slack.ts +80 -1
- package/Tests/Server/API/BaseAPI.test.ts +9 -4
- package/Tests/Server/Middleware/ProjectAuthorization.test.ts +133 -162
- package/Tests/Server/Services/ProbeService.test.ts +91 -784
- package/Tests/Server/Services/ScheduledMaintenanceService.test.ts +131 -112
- package/Tests/Server/Services/TeamMemberService.test.ts +87 -1343
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +18 -9
- package/Tests/Server/Utils/Cookie.test.ts +10 -2
- package/Tests/Types/HashedString.test.ts +52 -8
- package/Tests/UI/Components/404.test.tsx +10 -15
- package/Tests/UI/Components/Breadcrumbs.test.tsx +6 -2
- package/Tests/UI/Components/Button.test.tsx +12 -12
- package/Tests/UI/Components/Card.test.tsx +4 -2
- package/Tests/UI/Components/ConfirmModal.test.tsx +1 -1
- package/Tests/UI/Components/Dropdown.test.tsx +37 -4
- package/Tests/UI/Components/DuplicateModel.test.tsx +49 -45
- package/Tests/UI/Components/FilePicker.test.tsx +258 -178
- package/Tests/UI/Components/List.test.tsx +3 -1
- package/Tests/UI/Components/MarkdownEditor.test.tsx +6 -5
- package/Tests/UI/Components/MasterPage.test.tsx +1 -1
- package/Tests/UI/Components/Modal.test.tsx +5 -5
- package/Tests/UI/Components/NavBar.test.tsx +14 -1
- package/Tests/UI/Components/OrderedStatesList.test.tsx +1 -1
- package/Tests/UI/Components/Pagination.test.tsx +6 -2
- package/Tests/Utils/API.test.ts +133 -11
- package/Tests/__mocks__/azure.js +2 -0
- package/Tests/__mocks__/botbuilder-stdlib.js +2 -0
- package/Tests/__mocks__/botbuilder.js +10 -0
- package/Tests/__mocks__/locter.js +5 -0
- package/Tests/__mocks__/otpauth.js +30 -0
- package/Tests/__mocks__/simplewebauthn.js +34 -0
- package/Tests/__mocks__/styleMock.js +1 -0
- package/Tests/__mocks__/uuid.js +31 -0
- package/Tests/__mocks__/yaml.js +11 -0
- package/Tests/jest.setup.ts +14 -0
- package/UI/Components/AI/AITemplates.ts +226 -0
- package/UI/Components/AI/GenerateFromAIModal.tsx +21 -270
- package/build/dist/Server/Services/AIService.js +1 -1
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +59 -2
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +61 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Tests/Server/API/BaseAPI.test.js +7 -2
- package/build/dist/Tests/Server/API/BaseAPI.test.js.map +1 -1
- package/build/dist/Tests/Server/Middleware/ProjectAuthorization.test.js +89 -101
- package/build/dist/Tests/Server/Middleware/ProjectAuthorization.test.js.map +1 -1
- package/build/dist/Tests/Server/Services/ProbeService.test.js +95 -687
- package/build/dist/Tests/Server/Services/ProbeService.test.js.map +1 -1
- package/build/dist/Tests/Server/Services/ScheduledMaintenanceService.test.js +108 -89
- package/build/dist/Tests/Server/Services/ScheduledMaintenanceService.test.js.map +1 -1
- package/build/dist/Tests/Server/Services/TeamMemberService.test.js +85 -924
- package/build/dist/Tests/Server/Services/TeamMemberService.test.js.map +1 -1
- package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js +14 -9
- package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js.map +1 -1
- package/build/dist/Tests/Server/Utils/Cookie.test.js +10 -4
- package/build/dist/Tests/Server/Utils/Cookie.test.js.map +1 -1
- package/build/dist/Tests/Types/HashedString.test.js +39 -6
- package/build/dist/Tests/Types/HashedString.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/404.test.js +10 -10
- package/build/dist/Tests/UI/Components/404.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/Breadcrumbs.test.js +6 -2
- package/build/dist/Tests/UI/Components/Breadcrumbs.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/Button.test.js +12 -12
- package/build/dist/Tests/UI/Components/Card.test.js +4 -2
- package/build/dist/Tests/UI/Components/Card.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/ConfirmModal.test.js +1 -1
- package/build/dist/Tests/UI/Components/ConfirmModal.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/Dropdown.test.js +19 -3
- package/build/dist/Tests/UI/Components/Dropdown.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/DuplicateModel.test.js +46 -41
- package/build/dist/Tests/UI/Components/DuplicateModel.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/FilePicker.test.js +210 -117
- package/build/dist/Tests/UI/Components/FilePicker.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/List.test.js +3 -1
- package/build/dist/Tests/UI/Components/List.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/MarkdownEditor.test.js +6 -5
- package/build/dist/Tests/UI/Components/MarkdownEditor.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/MasterPage.test.js +1 -1
- package/build/dist/Tests/UI/Components/MasterPage.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/Modal.test.js +5 -5
- package/build/dist/Tests/UI/Components/Modal.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/NavBar.test.js +13 -1
- package/build/dist/Tests/UI/Components/NavBar.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/OrderedStatesList.test.js +1 -1
- package/build/dist/Tests/UI/Components/OrderedStatesList.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/Pagination.test.js +6 -2
- package/build/dist/Tests/UI/Components/Pagination.test.js.map +1 -1
- package/build/dist/Tests/Utils/API.test.js +100 -9
- package/build/dist/Tests/Utils/API.test.js.map +1 -1
- package/build/dist/Tests/jest.setup.js +13 -0
- package/build/dist/Tests/jest.setup.js.map +1 -0
- package/build/dist/UI/Components/AI/AITemplates.js +218 -0
- package/build/dist/UI/Components/AI/AITemplates.js.map +1 -0
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js +5 -238
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
- package/jest.config.json +18 -1
- package/package.json +1 -1
|
@@ -125,7 +125,7 @@ export class Service extends BaseService {
|
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
throw new BadDataException(
|
|
128
|
-
"Insufficient AI balance. Please recharge your AI balance in Project Settings >
|
|
128
|
+
"Insufficient AI balance. Please recharge your AI balance in Project Settings > AI Credits.",
|
|
129
129
|
);
|
|
130
130
|
}
|
|
131
131
|
}
|
|
@@ -434,12 +434,89 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
434
434
|
return { actionType: actionType as MicrosoftTeamsActionType, actionValue };
|
|
435
435
|
}
|
|
436
436
|
|
|
437
|
+
/**
|
|
438
|
+
* Converts markdown tables to HTML tables for Teams MessageCard.
|
|
439
|
+
* Teams MessageCard supports HTML in the text field.
|
|
440
|
+
*/
|
|
441
|
+
private static convertMarkdownTablesToHtml(markdown: string): string {
|
|
442
|
+
// Regular expression to match markdown tables
|
|
443
|
+
const tableRegex: RegExp =
|
|
444
|
+
/(?:^|\n)((?:\|[^\n]+\|\n)+(?:\|[-:\s|]+\|\n)(?:\|[^\n]+\|\n?)+)/g;
|
|
445
|
+
|
|
446
|
+
return markdown.replace(
|
|
447
|
+
tableRegex,
|
|
448
|
+
(_match: string, table: string): string => {
|
|
449
|
+
const lines: Array<string> = table.trim().split("\n");
|
|
450
|
+
|
|
451
|
+
if (lines.length < 2) {
|
|
452
|
+
return table;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Parse header row
|
|
456
|
+
const headerLine: string = lines[0] || "";
|
|
457
|
+
const headers: Array<string> = headerLine
|
|
458
|
+
.split("|")
|
|
459
|
+
.map((cell: string) => {
|
|
460
|
+
return cell.trim();
|
|
461
|
+
})
|
|
462
|
+
.filter((cell: string) => {
|
|
463
|
+
return cell.length > 0;
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// Skip separator line (line with dashes) and get data rows
|
|
467
|
+
const dataRows: Array<string> = lines.slice(2);
|
|
468
|
+
|
|
469
|
+
// Build HTML table
|
|
470
|
+
let html: string =
|
|
471
|
+
'<table style="border-collapse: collapse; width: 100%;">';
|
|
472
|
+
|
|
473
|
+
// Header row
|
|
474
|
+
html += "<tr>";
|
|
475
|
+
for (const header of headers) {
|
|
476
|
+
html += `<th style="border: 1px solid #ddd; padding: 8px; background-color: #f2f2f2; text-align: left;"><strong>${header}</strong></th>`;
|
|
477
|
+
}
|
|
478
|
+
html += "</tr>";
|
|
479
|
+
|
|
480
|
+
// Data rows
|
|
481
|
+
for (const row of dataRows) {
|
|
482
|
+
const cells: Array<string> = row
|
|
483
|
+
.split("|")
|
|
484
|
+
.map((cell: string) => {
|
|
485
|
+
return cell.trim();
|
|
486
|
+
})
|
|
487
|
+
.filter((cell: string) => {
|
|
488
|
+
return cell.length > 0;
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
if (cells.length === 0) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
html += "<tr>";
|
|
496
|
+
for (const cell of cells) {
|
|
497
|
+
html += `<td style="border: 1px solid #ddd; padding: 8px;">${cell}</td>`;
|
|
498
|
+
}
|
|
499
|
+
html += "</tr>";
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
html += "</table>";
|
|
503
|
+
|
|
504
|
+
return "\n" + html + "\n";
|
|
505
|
+
},
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
437
509
|
private static buildMessageCardFromMarkdown(markdown: string): JSONObject {
|
|
438
510
|
/*
|
|
439
511
|
* Teams MessageCard has limited markdown support. Headings like '##' are not supported
|
|
440
512
|
* and single newlines can collapse. Convert common patterns to a structured card.
|
|
441
513
|
*/
|
|
442
|
-
|
|
514
|
+
|
|
515
|
+
// First, convert markdown tables to HTML
|
|
516
|
+
const markdownWithHtmlTables: string =
|
|
517
|
+
this.convertMarkdownTablesToHtml(markdown);
|
|
518
|
+
|
|
519
|
+
const lines: Array<string> = markdownWithHtmlTables
|
|
443
520
|
.split("\n")
|
|
444
521
|
.map((l: string) => {
|
|
445
522
|
return l.trim();
|
|
@@ -1892,9 +1892,88 @@ export default class SlackUtil extends WorkspaceBase {
|
|
|
1892
1892
|
return apiResult;
|
|
1893
1893
|
}
|
|
1894
1894
|
|
|
1895
|
+
/**
|
|
1896
|
+
* Converts markdown tables to a Slack-friendly format.
|
|
1897
|
+
* Since Slack's mrkdwn doesn't support tables, we convert them to
|
|
1898
|
+
* a row-by-row format with bold headers.
|
|
1899
|
+
*/
|
|
1900
|
+
private static convertMarkdownTablesToSlackFormat(markdown: string): string {
|
|
1901
|
+
// Regular expression to match markdown tables
|
|
1902
|
+
const tableRegex: RegExp =
|
|
1903
|
+
/(?:^|\n)((?:\|[^\n]+\|\n)+(?:\|[-:\s|]+\|\n)(?:\|[^\n]+\|\n?)+)/g;
|
|
1904
|
+
|
|
1905
|
+
return markdown.replace(
|
|
1906
|
+
tableRegex,
|
|
1907
|
+
(_match: string, table: string): string => {
|
|
1908
|
+
const lines: Array<string> = table.trim().split("\n");
|
|
1909
|
+
|
|
1910
|
+
if (lines.length < 2) {
|
|
1911
|
+
return table;
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
// Parse header row
|
|
1915
|
+
const headerLine: string = lines[0] || "";
|
|
1916
|
+
const headers: Array<string> = headerLine
|
|
1917
|
+
.split("|")
|
|
1918
|
+
.map((cell: string) => {
|
|
1919
|
+
return cell.trim();
|
|
1920
|
+
})
|
|
1921
|
+
.filter((cell: string) => {
|
|
1922
|
+
return cell.length > 0;
|
|
1923
|
+
});
|
|
1924
|
+
|
|
1925
|
+
/*
|
|
1926
|
+
* Skip separator line (line with dashes)
|
|
1927
|
+
* Find data rows (skip header and separator)
|
|
1928
|
+
*/
|
|
1929
|
+
const dataRows: Array<string> = lines.slice(2);
|
|
1930
|
+
const formattedRows: Array<string> = [];
|
|
1931
|
+
|
|
1932
|
+
for (let rowIndex: number = 0; rowIndex < dataRows.length; rowIndex++) {
|
|
1933
|
+
const row: string = dataRows[rowIndex] || "";
|
|
1934
|
+
const cells: Array<string> = row
|
|
1935
|
+
.split("|")
|
|
1936
|
+
.map((cell: string) => {
|
|
1937
|
+
return cell.trim();
|
|
1938
|
+
})
|
|
1939
|
+
.filter((cell: string) => {
|
|
1940
|
+
return cell.length > 0;
|
|
1941
|
+
});
|
|
1942
|
+
|
|
1943
|
+
if (cells.length === 0) {
|
|
1944
|
+
continue;
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
const rowParts: Array<string> = [];
|
|
1948
|
+
for (
|
|
1949
|
+
let cellIndex: number = 0;
|
|
1950
|
+
cellIndex < cells.length;
|
|
1951
|
+
cellIndex++
|
|
1952
|
+
) {
|
|
1953
|
+
const header: string =
|
|
1954
|
+
headers[cellIndex] || `Column ${cellIndex + 1}`;
|
|
1955
|
+
const value: string = cells[cellIndex] || "";
|
|
1956
|
+
rowParts.push(`*${header}:* ${value}`);
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
if (dataRows.length > 1) {
|
|
1960
|
+
formattedRows.push(`_Row ${rowIndex + 1}_\n${rowParts.join("\n")}`);
|
|
1961
|
+
} else {
|
|
1962
|
+
formattedRows.push(rowParts.join("\n"));
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
return "\n" + formattedRows.join("\n\n") + "\n";
|
|
1967
|
+
},
|
|
1968
|
+
);
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1895
1971
|
@CaptureSpan()
|
|
1896
1972
|
public static convertMarkdownToSlackRichText(markdown: string): string {
|
|
1897
|
-
|
|
1973
|
+
// First convert tables to Slack-friendly format
|
|
1974
|
+
const markdownWithConvertedTables: string =
|
|
1975
|
+
this.convertMarkdownTablesToSlackFormat(markdown);
|
|
1976
|
+
return SlackifyMarkdown(markdownWithConvertedTables);
|
|
1898
1977
|
}
|
|
1899
1978
|
|
|
1900
1979
|
@CaptureSpan()
|
|
@@ -487,11 +487,16 @@ describe("BaseAPI", () => {
|
|
|
487
487
|
);
|
|
488
488
|
});
|
|
489
489
|
|
|
490
|
-
it("should
|
|
490
|
+
it("should use DEFAULT_LIMIT when limit is 0 (falsy value fallback)", async () => {
|
|
491
|
+
/*
|
|
492
|
+
* When limit is 0, parseInt("0", 10) returns 0 which is falsy,
|
|
493
|
+
* so the code falls back to DEFAULT_LIMIT via || operator
|
|
494
|
+
*/
|
|
491
495
|
emptyRequest.query["limit"] = "0";
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
496
|
+
// This should NOT throw since limit=0 is converted to DEFAULT_LIMIT
|
|
497
|
+
await expect(
|
|
498
|
+
baseApiInstance.getList(emptyRequest, res),
|
|
499
|
+
).resolves.toBeDefined();
|
|
495
500
|
});
|
|
496
501
|
});
|
|
497
502
|
|
|
@@ -1,26 +1,7 @@
|
|
|
1
1
|
import ProjectMiddleware from "../../../Server/Middleware/ProjectAuthorization";
|
|
2
|
-
import
|
|
3
|
-
import GlobalConfigService from "../../../Server/Services/GlobalConfigService";
|
|
4
|
-
import QueryHelper from "../../../Server/Types/Database/QueryHelper";
|
|
5
|
-
import {
|
|
6
|
-
ExpressRequest,
|
|
7
|
-
ExpressResponse,
|
|
8
|
-
NextFunction,
|
|
9
|
-
} from "../../../Server/Utils/Express";
|
|
10
|
-
import "../TestingUtils/Init";
|
|
11
|
-
import OneUptimeDate from "../../../Types/Date";
|
|
12
|
-
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
2
|
+
import { ExpressRequest } from "../../../Server/Utils/Express";
|
|
13
3
|
import ObjectID from "../../../Types/ObjectID";
|
|
14
|
-
import {
|
|
15
|
-
import ApiKey from "../../../Models/DatabaseModels/ApiKey";
|
|
16
|
-
import { describe, expect, afterEach, jest } from "@jest/globals";
|
|
17
|
-
import getJestMockFunction from "../../../Tests/MockType";
|
|
18
|
-
import { getJestSpyOn } from "../../../Tests/Spy";
|
|
19
|
-
import { TestDatabaseMock } from "../TestingUtils/__mocks__/TestDatabase.mock";
|
|
20
|
-
import APIKeyAccessPermission from "../../../Server/Utils/APIKey/AccessPermission";
|
|
21
|
-
|
|
22
|
-
jest.mock("../../../Server/Services/ApiKeyService");
|
|
23
|
-
jest.mock("../../../Server/Services/AccessTokenService");
|
|
4
|
+
import { describe, expect, test } from "@jest/globals";
|
|
24
5
|
|
|
25
6
|
type ObjectIdOrNull = ObjectID | null;
|
|
26
7
|
|
|
@@ -74,6 +55,30 @@ describe("ProjectMiddleware", () => {
|
|
|
74
55
|
|
|
75
56
|
expect(result).toBeNull();
|
|
76
57
|
});
|
|
58
|
+
|
|
59
|
+
test("should handle empty headers object", () => {
|
|
60
|
+
const req: Partial<ExpressRequest> = {
|
|
61
|
+
headers: {},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const result: ObjectIdOrNull = ProjectMiddleware.getProjectId(
|
|
65
|
+
req as ExpressRequest,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
expect(result).toBeNull();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("should handle empty params object", () => {
|
|
72
|
+
const req: Partial<ExpressRequest> = {
|
|
73
|
+
params: {},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const result: ObjectIdOrNull = ProjectMiddleware.getProjectId(
|
|
77
|
+
req as ExpressRequest,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
expect(result).toBeNull();
|
|
81
|
+
});
|
|
77
82
|
});
|
|
78
83
|
|
|
79
84
|
describe("getApiKey", () => {
|
|
@@ -96,200 +101,166 @@ describe("ProjectMiddleware", () => {
|
|
|
96
101
|
|
|
97
102
|
expect(result).toBeNull();
|
|
98
103
|
});
|
|
104
|
+
|
|
105
|
+
test("should handle empty headers", () => {
|
|
106
|
+
const req: Partial<ExpressRequest> = {
|
|
107
|
+
headers: {},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const result: ObjectIdOrNull = ProjectMiddleware.getApiKey(
|
|
111
|
+
req as ExpressRequest,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
expect(result).toBeNull();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("should handle undefined apikey header", () => {
|
|
118
|
+
const req: Partial<ExpressRequest> = {
|
|
119
|
+
headers: { apikey: undefined },
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const result: ObjectIdOrNull = ProjectMiddleware.getApiKey(
|
|
123
|
+
req as ExpressRequest,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
expect(result).toBeNull();
|
|
127
|
+
});
|
|
99
128
|
});
|
|
100
129
|
|
|
101
130
|
describe("hasApiKey", () => {
|
|
102
|
-
const req: ExpressRequest = { headers: {} } as ExpressRequest;
|
|
103
|
-
|
|
104
131
|
test("should return true when getApiKey returns a non-null value", () => {
|
|
105
|
-
req
|
|
132
|
+
const req: Partial<ExpressRequest> = {
|
|
133
|
+
headers: { apikey: mockedObjectId.toString() },
|
|
134
|
+
};
|
|
106
135
|
|
|
107
|
-
const result: boolean = ProjectMiddleware.hasApiKey(
|
|
136
|
+
const result: boolean = ProjectMiddleware.hasApiKey(
|
|
137
|
+
req as ExpressRequest,
|
|
138
|
+
);
|
|
108
139
|
|
|
109
140
|
expect(result).toStrictEqual(true);
|
|
110
141
|
});
|
|
111
142
|
|
|
112
143
|
test("should return false when getApiKey returns null", () => {
|
|
113
|
-
req
|
|
144
|
+
const req: Partial<ExpressRequest> = { headers: {} };
|
|
145
|
+
|
|
146
|
+
const result: boolean = ProjectMiddleware.hasApiKey(
|
|
147
|
+
req as ExpressRequest,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
expect(result).toStrictEqual(false);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("should return false for empty request", () => {
|
|
154
|
+
const req: Partial<ExpressRequest> = {};
|
|
114
155
|
|
|
115
|
-
const result: boolean = ProjectMiddleware.hasApiKey(
|
|
156
|
+
const result: boolean = ProjectMiddleware.hasApiKey(
|
|
157
|
+
req as ExpressRequest,
|
|
158
|
+
);
|
|
116
159
|
|
|
117
160
|
expect(result).toStrictEqual(false);
|
|
118
161
|
});
|
|
119
162
|
});
|
|
120
163
|
|
|
121
164
|
describe("hasProjectID", () => {
|
|
122
|
-
const req: ExpressRequest = { headers: {} } as ExpressRequest;
|
|
123
165
|
test("should return true when getProjectId returns a non-null value", () => {
|
|
124
|
-
req
|
|
166
|
+
const req: Partial<ExpressRequest> = {
|
|
167
|
+
headers: { tenantid: mockedObjectId.toString() },
|
|
168
|
+
};
|
|
125
169
|
|
|
126
|
-
const result: boolean = ProjectMiddleware.hasProjectID(
|
|
170
|
+
const result: boolean = ProjectMiddleware.hasProjectID(
|
|
171
|
+
req as ExpressRequest,
|
|
172
|
+
);
|
|
127
173
|
|
|
128
174
|
expect(result).toStrictEqual(true);
|
|
129
175
|
});
|
|
130
176
|
|
|
131
177
|
test("should return false when getProjectId returns null", () => {
|
|
132
|
-
req
|
|
178
|
+
const req: Partial<ExpressRequest> = { headers: {} };
|
|
133
179
|
|
|
134
|
-
const result: boolean = ProjectMiddleware.hasProjectID(
|
|
180
|
+
const result: boolean = ProjectMiddleware.hasProjectID(
|
|
181
|
+
req as ExpressRequest,
|
|
182
|
+
);
|
|
135
183
|
|
|
136
184
|
expect(result).toStrictEqual(false);
|
|
137
185
|
});
|
|
138
|
-
});
|
|
139
186
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const mockedApiModel: ApiKey = {
|
|
146
|
-
id: mockedObjectId,
|
|
147
|
-
projectId: mockedObjectId,
|
|
148
|
-
} as ApiKey;
|
|
149
|
-
|
|
150
|
-
beforeEach(
|
|
151
|
-
async () => {
|
|
152
|
-
jest.clearAllMocks();
|
|
153
|
-
next = getJestMockFunction();
|
|
154
|
-
await TestDatabaseMock.connectDbMock();
|
|
155
|
-
|
|
156
|
-
if (req.headers === undefined) {
|
|
157
|
-
req.headers = {};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
req.headers["tenantid"] = mockedObjectId.toString();
|
|
161
|
-
req.headers["apikey"] = mockedObjectId.toString();
|
|
162
|
-
},
|
|
163
|
-
10 * 1000, // 10 second timeout because setting up the DB is slow
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
afterEach(async () => {
|
|
167
|
-
await TestDatabaseMock.disconnectDbMock();
|
|
168
|
-
});
|
|
187
|
+
test("should return true when projectid is in header", () => {
|
|
188
|
+
const req: Partial<ExpressRequest> = {
|
|
189
|
+
headers: { projectid: mockedObjectId.toString() },
|
|
190
|
+
};
|
|
169
191
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
192
|
+
const result: boolean = ProjectMiddleware.hasProjectID(
|
|
193
|
+
req as ExpressRequest,
|
|
194
|
+
);
|
|
173
195
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
"findOneBy",
|
|
177
|
-
).mockResolvedValue(null);
|
|
196
|
+
expect(result).toStrictEqual(true);
|
|
197
|
+
});
|
|
178
198
|
|
|
179
|
-
|
|
180
|
-
req
|
|
199
|
+
test("should return true when projectId is in body", () => {
|
|
200
|
+
const req: Partial<ExpressRequest> = {
|
|
201
|
+
body: { projectId: mockedObjectId.toString() },
|
|
202
|
+
};
|
|
181
203
|
|
|
182
|
-
|
|
183
|
-
req,
|
|
184
|
-
res,
|
|
185
|
-
next,
|
|
204
|
+
const result: boolean = ProjectMiddleware.hasProjectID(
|
|
205
|
+
req as ExpressRequest,
|
|
186
206
|
);
|
|
187
207
|
|
|
188
|
-
expect(
|
|
189
|
-
query: {
|
|
190
|
-
_id: ObjectID.getZeroObjectID().toString(),
|
|
191
|
-
isMasterApiKeyEnabled: true,
|
|
192
|
-
masterApiKey: mockedObjectId,
|
|
193
|
-
},
|
|
194
|
-
props: {
|
|
195
|
-
isRoot: true,
|
|
196
|
-
},
|
|
197
|
-
select: {
|
|
198
|
-
_id: true,
|
|
199
|
-
},
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
expect(next).toHaveBeenCalledWith(
|
|
203
|
-
new BadDataException("Invalid API Key"),
|
|
204
|
-
);
|
|
208
|
+
expect(result).toStrictEqual(true);
|
|
205
209
|
});
|
|
206
210
|
|
|
207
|
-
test("should
|
|
208
|
-
req
|
|
211
|
+
test("should return true when tenantid is in params", () => {
|
|
212
|
+
const req: Partial<ExpressRequest> = {
|
|
213
|
+
params: { tenantid: mockedObjectId.toString() },
|
|
214
|
+
};
|
|
209
215
|
|
|
210
|
-
|
|
211
|
-
req,
|
|
212
|
-
res,
|
|
213
|
-
next,
|
|
216
|
+
const result: boolean = ProjectMiddleware.hasProjectID(
|
|
217
|
+
req as ExpressRequest,
|
|
214
218
|
);
|
|
215
219
|
|
|
216
|
-
expect(
|
|
217
|
-
new BadDataException(
|
|
218
|
-
"API Key not found in the request header. Please provide a valid API Key in the request header.",
|
|
219
|
-
),
|
|
220
|
-
);
|
|
220
|
+
expect(result).toStrictEqual(true);
|
|
221
221
|
});
|
|
222
222
|
|
|
223
|
-
test("should
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
).mockResolvedValue(null);
|
|
228
|
-
|
|
229
|
-
jest
|
|
230
|
-
.spyOn(QueryHelper, "greaterThan")
|
|
231
|
-
.mockImplementation(jest.fn() as any);
|
|
223
|
+
test("should return true when tenantid is in query", () => {
|
|
224
|
+
const req: Partial<ExpressRequest> = {
|
|
225
|
+
query: { tenantid: mockedObjectId.toString() },
|
|
226
|
+
};
|
|
232
227
|
|
|
233
|
-
|
|
234
|
-
req,
|
|
235
|
-
res,
|
|
236
|
-
next,
|
|
228
|
+
const result: boolean = ProjectMiddleware.hasProjectID(
|
|
229
|
+
req as ExpressRequest,
|
|
237
230
|
);
|
|
238
231
|
|
|
239
|
-
expect(
|
|
240
|
-
query: {
|
|
241
|
-
apiKey: mockedObjectId,
|
|
242
|
-
expiresAt: QueryHelper.greaterThan(OneUptimeDate.getCurrentDate()),
|
|
243
|
-
},
|
|
244
|
-
select: {
|
|
245
|
-
_id: true,
|
|
246
|
-
projectId: true,
|
|
247
|
-
},
|
|
248
|
-
props: { isRoot: true },
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
expect(next).toHaveBeenCalledWith(
|
|
252
|
-
new BadDataException("Invalid API Key"),
|
|
253
|
-
);
|
|
232
|
+
expect(result).toStrictEqual(true);
|
|
254
233
|
});
|
|
234
|
+
});
|
|
255
235
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
await ProjectMiddleware.isValidProjectIdAndApiKeyMiddleware(
|
|
264
|
-
req,
|
|
265
|
-
res,
|
|
266
|
-
next,
|
|
267
|
-
);
|
|
236
|
+
describe("ObjectID handling", () => {
|
|
237
|
+
test("should handle valid ObjectID string", () => {
|
|
238
|
+
const validId: ObjectID = ObjectID.generate();
|
|
239
|
+
const req: Partial<ExpressRequest> = {
|
|
240
|
+
headers: { tenantid: validId.toString() },
|
|
241
|
+
};
|
|
268
242
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
expect(next).toHaveBeenCalledWith(
|
|
272
|
-
new BadDataException("Invalid API Key"),
|
|
243
|
+
const result: ObjectIdOrNull = ProjectMiddleware.getProjectId(
|
|
244
|
+
req as ExpressRequest,
|
|
273
245
|
);
|
|
246
|
+
|
|
247
|
+
expect(result?.toString()).toBe(validId.toString());
|
|
274
248
|
});
|
|
275
249
|
|
|
276
|
-
test("should
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
req,
|
|
287
|
-
res,
|
|
288
|
-
next,
|
|
250
|
+
test("should handle multiple ID sources with priority", () => {
|
|
251
|
+
const headerId: ObjectID = ObjectID.generate();
|
|
252
|
+
const bodyId: ObjectID = ObjectID.generate();
|
|
253
|
+
const req: Partial<ExpressRequest> = {
|
|
254
|
+
headers: { tenantid: headerId.toString() },
|
|
255
|
+
body: { projectId: bodyId.toString() },
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const result: ObjectIdOrNull = ProjectMiddleware.getProjectId(
|
|
259
|
+
req as ExpressRequest,
|
|
289
260
|
);
|
|
290
261
|
|
|
291
|
-
|
|
292
|
-
expect(
|
|
262
|
+
// Headers should take priority
|
|
263
|
+
expect(result?.toString()).toBe(headerId.toString());
|
|
293
264
|
});
|
|
294
265
|
});
|
|
295
266
|
});
|