@devopsplaybook.io/common-utils 1.10.1-beta.23.b1906d7 → 1.11.0-beta.24.d8f9aef
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/README.md +73 -27
- package/dist/src/ConfigBase.d.ts +31 -0
- package/dist/src/ConfigBase.js +58 -16
- package/dist/src/DbUtils.d.ts +20 -3
- package/dist/src/DbUtils.js +93 -2
- package/dist/src/DbUtilsNoTelemetry.d.ts +4 -1
- package/dist/src/DbUtilsNoTelemetry.js +62 -6
- package/dist/src/PostgresDbUtils.d.ts +41 -10
- package/dist/src/PostgresDbUtils.js +357 -304
- package/dist/src/SqlDbUtils.d.ts +12 -4
- package/dist/src/SqlDbUtils.js +76 -30
- package/dist/src/users/Auth.d.ts +11 -1
- package/dist/src/users/Auth.js +160 -44
- package/dist/src/users/User.d.ts +10 -0
- package/dist/src/users/User.js +30 -10
- package/dist/src/users/UserApiToken.d.ts +4 -0
- package/dist/src/users/UserApiToken.js +11 -0
- package/dist/src/users/UsersApiTokensData.d.ts +12 -0
- package/dist/src/users/UsersApiTokensData.js +130 -33
- package/dist/src/users/UsersData.d.ts +20 -1
- package/dist/src/users/UsersData.js +150 -52
- package/dist/src/users/UsersRoutes.js +178 -61
- package/dist/src/users/index.d.ts +8 -0
- package/dist/src/users/index.js +24 -0
- package/package.json +58 -1
- package/.github/workflows/main-build.yml +0 -18
- package/.github/workflows/pr-check.yml +0 -27
- package/.github/workflows/reusable-merge-build.yml +0 -197
- package/.github/workflows/reusable-npm-merge.yml +0 -135
- package/.github/workflows/reusable-npm-pr.yml +0 -183
- package/.github/workflows/reusable-npm-upgrade.yml +0 -92
- package/.github/workflows/reusable-pr-verify.yml +0 -181
- package/AGENTS.md +0 -105
- package/index.ts +0 -18
- package/jest.config.js +0 -17
- package/prettierrc.json +0 -5
- package/src/ConfigBase.spec.ts +0 -108
- package/src/ConfigBase.ts +0 -297
- package/src/DbUtils.spec.ts +0 -23
- package/src/DbUtils.ts +0 -116
- package/src/DbUtilsNoTelemetry.spec.ts +0 -168
- package/src/DbUtilsNoTelemetry.ts +0 -117
- package/src/LLM.spec.ts +0 -303
- package/src/LLM.ts +0 -204
- package/src/Notifications.spec.ts +0 -265
- package/src/Notifications.ts +0 -201
- package/src/OTelContext.spec.ts +0 -58
- package/src/OTelContext.ts +0 -63
- package/src/PostgresDbUtils.spec.ts +0 -153
- package/src/PostgresDbUtils.ts +0 -666
- package/src/SqlDbUtils.spec.ts +0 -108
- package/src/SqlDbUtils.ts +0 -152
- package/src/SystemCommand.spec.ts +0 -18
- package/src/SystemCommand.ts +0 -23
- package/src/Timeout.spec.ts +0 -18
- package/src/Timeout.ts +0 -12
- package/src/users/Auth.spec.ts +0 -268
- package/src/users/Auth.ts +0 -202
- package/src/users/User.ts +0 -75
- package/src/users/UserApiToken.ts +0 -55
- package/src/users/UserPassword.spec.ts +0 -28
- package/src/users/UserPassword.ts +0 -20
- package/src/users/UserSession.ts +0 -9
- package/src/users/UsersApiTokensData.spec.ts +0 -158
- package/src/users/UsersApiTokensData.ts +0 -125
- package/src/users/UsersData.ts +0 -141
- package/src/users/UsersRoutes.ts +0 -374
- package/tsconfig.json +0 -15
- package/tsconfig.spec.json +0 -8
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
jest.mock("axios", () => ({
|
|
2
|
-
create: jest.fn(),
|
|
3
|
-
}));
|
|
4
|
-
|
|
5
|
-
import axios from "axios";
|
|
6
|
-
import {
|
|
7
|
-
NotificationsClient,
|
|
8
|
-
NotificationsLogger,
|
|
9
|
-
} from "./Notifications";
|
|
10
|
-
|
|
11
|
-
const mockedCreate = axios.create as jest.MockedFunction<
|
|
12
|
-
typeof axios.create
|
|
13
|
-
>;
|
|
14
|
-
|
|
15
|
-
/** Logger double that records every call. */
|
|
16
|
-
function createMockLogger(): NotificationsLogger & {
|
|
17
|
-
info: jest.Mock;
|
|
18
|
-
warn: jest.Mock;
|
|
19
|
-
error: jest.Mock;
|
|
20
|
-
} {
|
|
21
|
-
return {
|
|
22
|
-
info: jest.fn(),
|
|
23
|
-
warn: jest.fn(),
|
|
24
|
-
error: jest.fn(),
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
describe("NotificationsClient", () => {
|
|
29
|
-
let mockPost: jest.Mock;
|
|
30
|
-
let mockLogger: ReturnType<typeof createMockLogger>;
|
|
31
|
-
|
|
32
|
-
beforeEach(() => {
|
|
33
|
-
jest.clearAllMocks();
|
|
34
|
-
mockPost = jest.fn();
|
|
35
|
-
mockedCreate.mockReturnValue({ post: mockPost } as never);
|
|
36
|
-
mockLogger = createMockLogger();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe("constructor", () => {
|
|
40
|
-
it("should be enabled when apiEndpoint and apiToken are set", () => {
|
|
41
|
-
const client = new NotificationsClient({
|
|
42
|
-
apiEndpoint: "http://localhost/api/notifications",
|
|
43
|
-
apiToken: "token",
|
|
44
|
-
logger: mockLogger,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
expect(client.isEnabled()).toBe(true);
|
|
48
|
-
expect(mockedCreate).toHaveBeenCalledTimes(1);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("should be disabled when apiEndpoint is empty", () => {
|
|
52
|
-
const client = new NotificationsClient({
|
|
53
|
-
apiEndpoint: "",
|
|
54
|
-
apiToken: "token",
|
|
55
|
-
logger: mockLogger,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
expect(client.isEnabled()).toBe(false);
|
|
59
|
-
expect(mockedCreate).not.toHaveBeenCalled();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("should be disabled when apiToken is empty", () => {
|
|
63
|
-
const client = new NotificationsClient({
|
|
64
|
-
apiEndpoint: "http://localhost/api/notifications",
|
|
65
|
-
apiToken: "",
|
|
66
|
-
logger: mockLogger,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
expect(client.isEnabled()).toBe(false);
|
|
70
|
-
expect(mockedCreate).not.toHaveBeenCalled();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("should log the enabled status exactly once at construction", () => {
|
|
74
|
-
new NotificationsClient({
|
|
75
|
-
apiEndpoint: "http://localhost/api/notifications",
|
|
76
|
-
apiToken: "token",
|
|
77
|
-
logger: mockLogger,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
81
|
-
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
82
|
-
"Notifications integration enabled",
|
|
83
|
-
);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("should log the disabled status exactly once at construction", () => {
|
|
87
|
-
new NotificationsClient({
|
|
88
|
-
apiEndpoint: "",
|
|
89
|
-
apiToken: "",
|
|
90
|
-
logger: mockLogger,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
94
|
-
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
95
|
-
"Notifications integration disabled (apiEndpoint or apiToken not set)",
|
|
96
|
-
);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("should fall back to console logging when no logger is provided", () => {
|
|
100
|
-
const logSpy = jest.spyOn(console, "log").mockImplementation(jest.fn());
|
|
101
|
-
|
|
102
|
-
const client = new NotificationsClient({
|
|
103
|
-
apiEndpoint: "http://localhost/api/notifications",
|
|
104
|
-
apiToken: "token",
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
expect(client.isEnabled()).toBe(true);
|
|
108
|
-
expect(logSpy).toHaveBeenCalledWith(
|
|
109
|
-
"Notifications integration enabled",
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
logSpy.mockRestore();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe("send", () => {
|
|
117
|
-
it("should return null silently when disabled", async () => {
|
|
118
|
-
const client = new NotificationsClient({
|
|
119
|
-
apiEndpoint: "",
|
|
120
|
-
apiToken: "",
|
|
121
|
-
logger: mockLogger,
|
|
122
|
-
});
|
|
123
|
-
jest.clearAllMocks(); // drop the startup log
|
|
124
|
-
|
|
125
|
-
const result = await client.send({ title: "Test" });
|
|
126
|
-
|
|
127
|
-
expect(result).toBeNull();
|
|
128
|
-
expect(mockPost).not.toHaveBeenCalled();
|
|
129
|
-
expect(mockLogger.info).not.toHaveBeenCalled();
|
|
130
|
-
expect(mockLogger.warn).not.toHaveBeenCalled();
|
|
131
|
-
expect(mockLogger.error).not.toHaveBeenCalled();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("should post with default values when payload fields are omitted", async () => {
|
|
135
|
-
const client = new NotificationsClient({
|
|
136
|
-
apiEndpoint: "http://localhost/api/notifications",
|
|
137
|
-
apiToken: "token",
|
|
138
|
-
logger: mockLogger,
|
|
139
|
-
});
|
|
140
|
-
const response = {
|
|
141
|
-
id: "id-1",
|
|
142
|
-
title: "Test",
|
|
143
|
-
body: "",
|
|
144
|
-
source: "api",
|
|
145
|
-
severity: "info",
|
|
146
|
-
data: "{}",
|
|
147
|
-
createdAt: "2026-08-26T00:00:00.000Z",
|
|
148
|
-
};
|
|
149
|
-
mockPost.mockResolvedValue({ data: response });
|
|
150
|
-
|
|
151
|
-
const result = await client.send({ title: "Test" });
|
|
152
|
-
|
|
153
|
-
expect(mockPost).toHaveBeenCalledWith("/", {
|
|
154
|
-
title: "Test",
|
|
155
|
-
body: "",
|
|
156
|
-
source: "api",
|
|
157
|
-
severity: "info",
|
|
158
|
-
data: "{}",
|
|
159
|
-
});
|
|
160
|
-
expect(result).toEqual(response);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it("should pass through explicit payload values", async () => {
|
|
164
|
-
const client = new NotificationsClient({
|
|
165
|
-
apiEndpoint: "http://localhost/api/notifications",
|
|
166
|
-
apiToken: "token",
|
|
167
|
-
logger: mockLogger,
|
|
168
|
-
});
|
|
169
|
-
mockPost.mockResolvedValue({ data: {} });
|
|
170
|
-
|
|
171
|
-
await client.send({
|
|
172
|
-
title: "Alert",
|
|
173
|
-
body: "Something happened",
|
|
174
|
-
source: "my-app",
|
|
175
|
-
severity: "error",
|
|
176
|
-
data: '{"key":"value"}',
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
expect(mockPost).toHaveBeenCalledWith("/", {
|
|
180
|
-
title: "Alert",
|
|
181
|
-
body: "Something happened",
|
|
182
|
-
source: "my-app",
|
|
183
|
-
severity: "error",
|
|
184
|
-
data: '{"key":"value"}',
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it("should return null and log an error when the request fails", async () => {
|
|
189
|
-
const client = new NotificationsClient({
|
|
190
|
-
apiEndpoint: "http://localhost/api/notifications",
|
|
191
|
-
apiToken: "token",
|
|
192
|
-
logger: mockLogger,
|
|
193
|
-
});
|
|
194
|
-
const failure = new Error("network down");
|
|
195
|
-
mockPost.mockRejectedValue(failure);
|
|
196
|
-
|
|
197
|
-
const result = await client.send({ title: "Test" });
|
|
198
|
-
|
|
199
|
-
expect(result).toBeNull();
|
|
200
|
-
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
201
|
-
"NotificationsClient: failed to send notification",
|
|
202
|
-
failure,
|
|
203
|
-
);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it("should not reject when the request fails", async () => {
|
|
207
|
-
const client = new NotificationsClient({
|
|
208
|
-
apiEndpoint: "http://localhost/api/notifications",
|
|
209
|
-
apiToken: "token",
|
|
210
|
-
logger: mockLogger,
|
|
211
|
-
});
|
|
212
|
-
mockPost.mockRejectedValue(new Error("network down"));
|
|
213
|
-
|
|
214
|
-
await expect(client.send({ title: "Test" })).resolves.toBeNull();
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
describe("severity helpers", () => {
|
|
219
|
-
it.each([
|
|
220
|
-
["info", "info"],
|
|
221
|
-
["success", "success"],
|
|
222
|
-
["warning", "warning"],
|
|
223
|
-
["error", "error"],
|
|
224
|
-
] as const)(
|
|
225
|
-
"should send a %s notification",
|
|
226
|
-
async (method, severity) => {
|
|
227
|
-
const client = new NotificationsClient({
|
|
228
|
-
apiEndpoint: "http://localhost/api/notifications",
|
|
229
|
-
apiToken: "token",
|
|
230
|
-
logger: mockLogger,
|
|
231
|
-
});
|
|
232
|
-
mockPost.mockResolvedValue({ data: {} });
|
|
233
|
-
|
|
234
|
-
await client[method]("Title", "Body", "my-app");
|
|
235
|
-
|
|
236
|
-
expect(mockPost).toHaveBeenCalledWith(
|
|
237
|
-
"/",
|
|
238
|
-
expect.objectContaining({
|
|
239
|
-
title: "Title",
|
|
240
|
-
body: "Body",
|
|
241
|
-
source: "my-app",
|
|
242
|
-
severity,
|
|
243
|
-
}),
|
|
244
|
-
);
|
|
245
|
-
},
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
it("should return null silently from helpers when disabled", async () => {
|
|
249
|
-
const client = new NotificationsClient({
|
|
250
|
-
apiEndpoint: "",
|
|
251
|
-
apiToken: "",
|
|
252
|
-
logger: mockLogger,
|
|
253
|
-
});
|
|
254
|
-
jest.clearAllMocks(); // drop the startup log
|
|
255
|
-
|
|
256
|
-
expect(await client.info("Title")).toBeNull();
|
|
257
|
-
expect(await client.success("Title")).toBeNull();
|
|
258
|
-
expect(await client.warning("Title")).toBeNull();
|
|
259
|
-
expect(await client.error("Title")).toBeNull();
|
|
260
|
-
expect(mockPost).not.toHaveBeenCalled();
|
|
261
|
-
expect(mockLogger.info).not.toHaveBeenCalled();
|
|
262
|
-
expect(mockLogger.error).not.toHaveBeenCalled();
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
});
|
package/src/Notifications.ts
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import axios, { AxiosInstance } from "axios";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Severity levels for notifications.
|
|
5
|
-
*/
|
|
6
|
-
export type NotificationSeverity = "info" | "warning" | "error" | "success";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Minimal logger interface expected by the notifications client.
|
|
10
|
-
* Matches the subset of the OTel logger used by devopsplaybook.io projects.
|
|
11
|
-
*/
|
|
12
|
-
export interface NotificationsLogger {
|
|
13
|
-
info(message: string): void;
|
|
14
|
-
warn(message: string): void;
|
|
15
|
-
error(message: string, err?: unknown): void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Configuration for {@link NotificationsClient}.
|
|
20
|
-
*/
|
|
21
|
-
export interface NotificationsConfig {
|
|
22
|
-
/** API endpoint URL (e.g., "https://notifications.example.com/api/notifications") */
|
|
23
|
-
apiEndpoint: string;
|
|
24
|
-
/** API token used for Bearer authentication */
|
|
25
|
-
apiToken: string;
|
|
26
|
-
/** Optional logger; falls back to console when omitted */
|
|
27
|
-
logger?: NotificationsLogger;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Payload for creating a notification.
|
|
32
|
-
*/
|
|
33
|
-
export interface NotificationPayload {
|
|
34
|
-
/** Notification title */
|
|
35
|
-
title: string;
|
|
36
|
-
/** Notification body/content */
|
|
37
|
-
body?: string;
|
|
38
|
-
/** Source identifier (defaults to "api") */
|
|
39
|
-
source?: string;
|
|
40
|
-
/** Severity level (defaults to "info") */
|
|
41
|
-
severity?: NotificationSeverity;
|
|
42
|
-
/** Additional data as a JSON string */
|
|
43
|
-
data?: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Response from the notifications API.
|
|
48
|
-
*/
|
|
49
|
-
export interface NotificationResponse {
|
|
50
|
-
id: string;
|
|
51
|
-
title: string;
|
|
52
|
-
body: string;
|
|
53
|
-
source: string;
|
|
54
|
-
severity: string;
|
|
55
|
-
data: string;
|
|
56
|
-
createdAt: string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** Console fallback used when no logger is injected. */
|
|
60
|
-
const consoleLogger: NotificationsLogger = {
|
|
61
|
-
info: (message: string) => console.log(message),
|
|
62
|
-
warn: (message: string) => console.warn(message),
|
|
63
|
-
error: (message: string, err?: unknown) => console.error(message, err),
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Client for sending notifications to the central notifications service.
|
|
68
|
-
*
|
|
69
|
-
* The client is fail-safe by design:
|
|
70
|
-
*
|
|
71
|
-
* - It is disabled (and never throws) when `apiEndpoint` or `apiToken` is
|
|
72
|
-
* missing, so a partially configured parent application still starts.
|
|
73
|
-
* - The integration status is logged exactly once, at construction time.
|
|
74
|
-
* - Follow-up `send` calls on a disabled client are silent and resolve to
|
|
75
|
-
* `null`.
|
|
76
|
-
* - Sending errors are logged and resolve to `null` instead of rejecting.
|
|
77
|
-
*
|
|
78
|
-
* @example
|
|
79
|
-
* ```ts
|
|
80
|
-
* const client = new NotificationsClient({
|
|
81
|
-
* apiEndpoint: config.NOTIFICATIONS_API,
|
|
82
|
-
* apiToken: config.NOTIFICATIONS_TOKEN,
|
|
83
|
-
* logger: OTelLogger().createModuleLogger("notifications"),
|
|
84
|
-
* });
|
|
85
|
-
*
|
|
86
|
-
* await client.send({
|
|
87
|
-
* title: "Deployment finished",
|
|
88
|
-
* body: "Version 1.2.3 deployed to production",
|
|
89
|
-
* source: "my-app",
|
|
90
|
-
* severity: "success",
|
|
91
|
-
* });
|
|
92
|
-
* ```
|
|
93
|
-
*/
|
|
94
|
-
export class NotificationsClient {
|
|
95
|
-
private client: AxiosInstance | null = null;
|
|
96
|
-
private readonly enabled: boolean;
|
|
97
|
-
private readonly logger: NotificationsLogger;
|
|
98
|
-
|
|
99
|
-
constructor(config: NotificationsConfig) {
|
|
100
|
-
this.enabled = !!(config.apiEndpoint && config.apiToken);
|
|
101
|
-
this.logger = config.logger || consoleLogger;
|
|
102
|
-
|
|
103
|
-
if (this.enabled) {
|
|
104
|
-
this.client = axios.create({
|
|
105
|
-
baseURL: config.apiEndpoint,
|
|
106
|
-
headers: {
|
|
107
|
-
"Content-Type": "application/json",
|
|
108
|
-
Authorization: `Bearer ${config.apiToken}`,
|
|
109
|
-
},
|
|
110
|
-
timeout: 10000,
|
|
111
|
-
});
|
|
112
|
-
this.logger.info("Notifications integration enabled");
|
|
113
|
-
} else {
|
|
114
|
-
this.logger.info(
|
|
115
|
-
"Notifications integration disabled (apiEndpoint or apiToken not set)",
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Check whether the client is properly configured.
|
|
122
|
-
*/
|
|
123
|
-
public isEnabled(): boolean {
|
|
124
|
-
return this.enabled;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Send a notification.
|
|
129
|
-
*
|
|
130
|
-
* @param payload The notification payload.
|
|
131
|
-
* @returns The created notification, or `null` when disabled or on failure.
|
|
132
|
-
*/
|
|
133
|
-
public async send(
|
|
134
|
-
payload: NotificationPayload,
|
|
135
|
-
): Promise<NotificationResponse | null> {
|
|
136
|
-
if (!this.enabled || !this.client) {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
const response = await this.client.post<NotificationResponse>("/", {
|
|
142
|
-
title: payload.title,
|
|
143
|
-
body: payload.body || "",
|
|
144
|
-
source: payload.source || "api",
|
|
145
|
-
severity: payload.severity || "info",
|
|
146
|
-
data: payload.data || "{}",
|
|
147
|
-
});
|
|
148
|
-
return response.data;
|
|
149
|
-
} catch (err) {
|
|
150
|
-
this.logger.error(
|
|
151
|
-
"NotificationsClient: failed to send notification",
|
|
152
|
-
err,
|
|
153
|
-
);
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Send an info notification.
|
|
160
|
-
*/
|
|
161
|
-
public async info(
|
|
162
|
-
title: string,
|
|
163
|
-
body?: string,
|
|
164
|
-
source?: string,
|
|
165
|
-
): Promise<NotificationResponse | null> {
|
|
166
|
-
return this.send({ title, body, source, severity: "info" });
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Send a success notification.
|
|
171
|
-
*/
|
|
172
|
-
public async success(
|
|
173
|
-
title: string,
|
|
174
|
-
body?: string,
|
|
175
|
-
source?: string,
|
|
176
|
-
): Promise<NotificationResponse | null> {
|
|
177
|
-
return this.send({ title, body, source, severity: "success" });
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Send a warning notification.
|
|
182
|
-
*/
|
|
183
|
-
public async warning(
|
|
184
|
-
title: string,
|
|
185
|
-
body?: string,
|
|
186
|
-
source?: string,
|
|
187
|
-
): Promise<NotificationResponse | null> {
|
|
188
|
-
return this.send({ title, body, source, severity: "warning" });
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Send an error notification.
|
|
193
|
-
*/
|
|
194
|
-
public async error(
|
|
195
|
-
title: string,
|
|
196
|
-
body?: string,
|
|
197
|
-
source?: string,
|
|
198
|
-
): Promise<NotificationResponse | null> {
|
|
199
|
-
return this.send({ title, body, source, severity: "error" });
|
|
200
|
-
}
|
|
201
|
-
}
|
package/src/OTelContext.spec.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { createOTelContext } from "./OTelContext";
|
|
2
|
-
|
|
3
|
-
describe("createOTelContext", () => {
|
|
4
|
-
it("should return an object with all expected methods", () => {
|
|
5
|
-
const ctx = createOTelContext();
|
|
6
|
-
expect(typeof ctx.OTelTracer).toBe("function");
|
|
7
|
-
expect(typeof ctx.OTelSetTracer).toBe("function");
|
|
8
|
-
expect(typeof ctx.OTelMeter).toBe("function");
|
|
9
|
-
expect(typeof ctx.OTelSetMeter).toBe("function");
|
|
10
|
-
expect(typeof ctx.OTelLogger).toBe("function");
|
|
11
|
-
expect(typeof ctx.OTelRequestSpan).toBe("function");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("should create a StandardLogger lazily", () => {
|
|
15
|
-
const ctx = createOTelContext();
|
|
16
|
-
const logger = ctx.OTelLogger();
|
|
17
|
-
expect(logger).toBeDefined();
|
|
18
|
-
// Same instance on second call
|
|
19
|
-
expect(ctx.OTelLogger()).toBe(logger);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("should store and retrieve tracer via setter/getter", () => {
|
|
23
|
-
const ctx = createOTelContext();
|
|
24
|
-
const fakeTracer = { id: "tracer-1" } as never;
|
|
25
|
-
ctx.OTelSetTracer(fakeTracer);
|
|
26
|
-
expect(ctx.OTelTracer()).toBe(fakeTracer);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("should store and retrieve meter via setter/getter", () => {
|
|
30
|
-
const ctx = createOTelContext();
|
|
31
|
-
const fakeMeter = { id: "meter-1" } as never;
|
|
32
|
-
ctx.OTelSetMeter(fakeMeter);
|
|
33
|
-
expect(ctx.OTelMeter()).toBe(fakeMeter);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("should isolate contexts from each other", () => {
|
|
37
|
-
const ctx1 = createOTelContext();
|
|
38
|
-
const ctx2 = createOTelContext();
|
|
39
|
-
const fakeTracer1 = { id: "tracer-1" } as never;
|
|
40
|
-
const fakeTracer2 = { id: "tracer-2" } as never;
|
|
41
|
-
ctx1.OTelSetTracer(fakeTracer1);
|
|
42
|
-
ctx2.OTelSetTracer(fakeTracer2);
|
|
43
|
-
expect(ctx1.OTelTracer()).toBe(fakeTracer1);
|
|
44
|
-
expect(ctx2.OTelTracer()).toBe(fakeTracer2);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("OTelRequestSpan should return undefined for object without tracerSpanApi", () => {
|
|
48
|
-
const ctx = createOTelContext();
|
|
49
|
-
expect(ctx.OTelRequestSpan({})).toBeUndefined();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("OTelRequestSpan should return the span when present", () => {
|
|
53
|
-
const ctx = createOTelContext();
|
|
54
|
-
const fakeSpan = { spanId: "123" } as never;
|
|
55
|
-
const req = { tracerSpanApi: fakeSpan };
|
|
56
|
-
expect(ctx.OTelRequestSpan(req)).toBe(fakeSpan);
|
|
57
|
-
});
|
|
58
|
-
});
|
package/src/OTelContext.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
StandardLogger,
|
|
3
|
-
StandardMeter,
|
|
4
|
-
StandardTracer,
|
|
5
|
-
} from "@devopsplaybook.io/otel-utils";
|
|
6
|
-
import { Span } from "@opentelemetry/sdk-trace-base";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Result of {@link createOTelContext}.
|
|
10
|
-
* Holds module-level singletons for the OpenTelemetry tracer, meter and logger
|
|
11
|
-
* used throughout a server process.
|
|
12
|
-
*/
|
|
13
|
-
export interface OTelContext {
|
|
14
|
-
OTelTracer: () => StandardTracer;
|
|
15
|
-
OTelSetTracer: (tracer: StandardTracer) => void;
|
|
16
|
-
OTelMeter: () => StandardMeter;
|
|
17
|
-
OTelSetMeter: (meter: StandardMeter) => void;
|
|
18
|
-
OTelLogger: () => StandardLogger;
|
|
19
|
-
/**
|
|
20
|
-
* Retrieves the span previously attached to a request object.
|
|
21
|
-
* Equivalent to `req.tracerSpanApi`.
|
|
22
|
-
*/
|
|
23
|
-
OTelRequestSpan: (req: any) => Span | undefined;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Creates an isolated OTel context (tracer / meter / logger singletons).
|
|
28
|
-
*
|
|
29
|
-
* Each server process should call this once at startup and pass the returned
|
|
30
|
-
* object to modules that need telemetry access. This avoids polluting the
|
|
31
|
-
* global module scope and makes unit-testing straightforward.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```ts
|
|
35
|
-
* const otel = createOTelContext();
|
|
36
|
-
* otel.OTelSetTracer(new StandardTracer(config));
|
|
37
|
-
* otel.OTelSetMeter(new StandardMeter(config));
|
|
38
|
-
* otel.OTelLogger().initOTel(config);
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export function createOTelContext(): OTelContext {
|
|
42
|
-
let tracer: StandardTracer;
|
|
43
|
-
let meter: StandardMeter;
|
|
44
|
-
let logger: StandardLogger;
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
OTelTracer: () => tracer,
|
|
48
|
-
OTelSetTracer: (t: StandardTracer) => {
|
|
49
|
-
tracer = t;
|
|
50
|
-
},
|
|
51
|
-
OTelMeter: () => meter,
|
|
52
|
-
OTelSetMeter: (m: StandardMeter) => {
|
|
53
|
-
meter = m;
|
|
54
|
-
},
|
|
55
|
-
OTelLogger: () => {
|
|
56
|
-
if (!logger) {
|
|
57
|
-
logger = new StandardLogger();
|
|
58
|
-
}
|
|
59
|
-
return logger;
|
|
60
|
-
},
|
|
61
|
-
OTelRequestSpan: (req: any) => req?.tracerSpanApi as Span | undefined,
|
|
62
|
-
};
|
|
63
|
-
}
|