@terreno/api 0.0.1
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/LICENSE +202 -0
- package/README.md +170 -0
- package/biome.jsonc +22 -0
- package/bunfig.toml +4 -0
- package/dist/api.d.ts +227 -0
- package/dist/api.js +1024 -0
- package/dist/api.test.d.ts +1 -0
- package/dist/api.test.js +2143 -0
- package/dist/auth.d.ts +50 -0
- package/dist/auth.js +512 -0
- package/dist/auth.test.d.ts +1 -0
- package/dist/auth.test.js +778 -0
- package/dist/errors.d.ts +75 -0
- package/dist/errors.js +216 -0
- package/dist/example.d.ts +1 -0
- package/dist/example.js +118 -0
- package/dist/expressServer.d.ts +35 -0
- package/dist/expressServer.js +436 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +30 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.js +249 -0
- package/dist/middleware.d.ts +10 -0
- package/dist/middleware.js +52 -0
- package/dist/notifiers/googleChatNotifier.d.ts +5 -0
- package/dist/notifiers/googleChatNotifier.js +130 -0
- package/dist/notifiers/googleChatNotifier.test.d.ts +1 -0
- package/dist/notifiers/googleChatNotifier.test.js +260 -0
- package/dist/notifiers/index.d.ts +3 -0
- package/dist/notifiers/index.js +19 -0
- package/dist/notifiers/slackNotifier.d.ts +5 -0
- package/dist/notifiers/slackNotifier.js +130 -0
- package/dist/notifiers/slackNotifier.test.d.ts +1 -0
- package/dist/notifiers/slackNotifier.test.js +259 -0
- package/dist/notifiers/zoomNotifier.d.ts +34 -0
- package/dist/notifiers/zoomNotifier.js +181 -0
- package/dist/notifiers/zoomNotifier.test.d.ts +1 -0
- package/dist/notifiers/zoomNotifier.test.js +370 -0
- package/dist/openApi.d.ts +60 -0
- package/dist/openApi.js +441 -0
- package/dist/openApi.test.d.ts +1 -0
- package/dist/openApi.test.js +445 -0
- package/dist/openApiBuilder.d.ts +419 -0
- package/dist/openApiBuilder.js +424 -0
- package/dist/openApiBuilder.test.d.ts +1 -0
- package/dist/openApiBuilder.test.js +509 -0
- package/dist/openApiEtag.d.ts +7 -0
- package/dist/openApiEtag.js +38 -0
- package/dist/permissions.d.ts +26 -0
- package/dist/permissions.js +331 -0
- package/dist/permissions.test.d.ts +1 -0
- package/dist/permissions.test.js +413 -0
- package/dist/plugins.d.ts +67 -0
- package/dist/plugins.js +315 -0
- package/dist/plugins.test.d.ts +1 -0
- package/dist/plugins.test.js +639 -0
- package/dist/populate.d.ts +14 -0
- package/dist/populate.js +315 -0
- package/dist/populate.test.d.ts +1 -0
- package/dist/populate.test.js +133 -0
- package/dist/response.d.ts +0 -0
- package/dist/response.js +1 -0
- package/dist/tests/bunSetup.d.ts +1 -0
- package/dist/tests/bunSetup.js +297 -0
- package/dist/tests/index.d.ts +1 -0
- package/dist/tests/index.js +17 -0
- package/dist/tests.d.ts +99 -0
- package/dist/tests.js +273 -0
- package/dist/transformers.d.ts +25 -0
- package/dist/transformers.js +217 -0
- package/dist/transformers.test.d.ts +1 -0
- package/dist/transformers.test.js +370 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +143 -0
- package/dist/utils.test.d.ts +1 -0
- package/dist/utils.test.js +14 -0
- package/index.ts +1 -0
- package/package.json +88 -0
- package/src/__snapshots__/openApi.test.ts.snap +4814 -0
- package/src/__snapshots__/openApiBuilder.test.ts.snap +1485 -0
- package/src/api.test.ts +1661 -0
- package/src/api.ts +1036 -0
- package/src/auth.test.ts +550 -0
- package/src/auth.ts +408 -0
- package/src/errors.ts +225 -0
- package/src/example.ts +99 -0
- package/src/express.d.ts +5 -0
- package/src/expressServer.ts +387 -0
- package/src/index.ts +14 -0
- package/src/logger.ts +190 -0
- package/src/middleware.ts +18 -0
- package/src/notifiers/googleChatNotifier.test.ts +114 -0
- package/src/notifiers/googleChatNotifier.ts +47 -0
- package/src/notifiers/index.ts +3 -0
- package/src/notifiers/slackNotifier.test.ts +113 -0
- package/src/notifiers/slackNotifier.ts +55 -0
- package/src/notifiers/zoomNotifier.test.ts +207 -0
- package/src/notifiers/zoomNotifier.ts +111 -0
- package/src/openApi.test.ts +331 -0
- package/src/openApi.ts +494 -0
- package/src/openApiBuilder.test.ts +442 -0
- package/src/openApiBuilder.ts +636 -0
- package/src/openApiEtag.ts +40 -0
- package/src/permissions.test.ts +219 -0
- package/src/permissions.ts +228 -0
- package/src/plugins.test.ts +390 -0
- package/src/plugins.ts +289 -0
- package/src/populate.test.ts +65 -0
- package/src/populate.ts +258 -0
- package/src/response.ts +0 -0
- package/src/tests/bunSetup.ts +234 -0
- package/src/tests/index.ts +1 -0
- package/src/tests.ts +218 -0
- package/src/transformers.test.ts +202 -0
- package/src/transformers.ts +170 -0
- package/src/utils.test.ts +14 -0
- package/src/utils.ts +47 -0
- package/tsconfig.json +60 -0
- package/types.d.ts +17 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
47
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
48
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
49
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
50
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
51
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
52
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
56
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
57
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
58
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
59
|
+
function step(op) {
|
|
60
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
61
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
62
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
63
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
64
|
+
switch (op[0]) {
|
|
65
|
+
case 0: case 1: t = op; break;
|
|
66
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
67
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
68
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
69
|
+
default:
|
|
70
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
71
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
72
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
73
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
74
|
+
if (t[2]) _.ops.pop();
|
|
75
|
+
_.trys.pop(); continue;
|
|
76
|
+
}
|
|
77
|
+
op = body.call(thisArg, _);
|
|
78
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
79
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
83
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
84
|
+
if (!m) return o;
|
|
85
|
+
var i = m.call(o), r, ar = [], e;
|
|
86
|
+
try {
|
|
87
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
88
|
+
}
|
|
89
|
+
catch (error) { e = { error: error }; }
|
|
90
|
+
finally {
|
|
91
|
+
try {
|
|
92
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
93
|
+
}
|
|
94
|
+
finally { if (e) throw e.error; }
|
|
95
|
+
}
|
|
96
|
+
return ar;
|
|
97
|
+
};
|
|
98
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
99
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
100
|
+
};
|
|
101
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
102
|
+
var bun_test_1 = require("bun:test");
|
|
103
|
+
var Sentry = __importStar(require("@sentry/node"));
|
|
104
|
+
var axios_1 = __importDefault(require("axios"));
|
|
105
|
+
var slackNotifier_1 = require("./slackNotifier");
|
|
106
|
+
(0, bun_test_1.describe)("sendToSlack", function () {
|
|
107
|
+
var mockAxiosPost;
|
|
108
|
+
var ORIGINAL_ENV = process.env;
|
|
109
|
+
(0, bun_test_1.beforeEach)(function () {
|
|
110
|
+
mockAxiosPost = (0, bun_test_1.spyOn)(axios_1.default, "post").mockResolvedValue({ status: 200 });
|
|
111
|
+
process.env = __assign({}, ORIGINAL_ENV);
|
|
112
|
+
process.env.SLACK_WEBHOOKS = undefined;
|
|
113
|
+
Sentry.captureException.mockClear();
|
|
114
|
+
Sentry.captureMessage.mockClear();
|
|
115
|
+
});
|
|
116
|
+
(0, bun_test_1.afterEach)(function () {
|
|
117
|
+
mockAxiosPost.mockRestore();
|
|
118
|
+
});
|
|
119
|
+
(0, bun_test_1.afterAll)(function () {
|
|
120
|
+
process.env = ORIGINAL_ENV;
|
|
121
|
+
});
|
|
122
|
+
(0, bun_test_1.it)("returns early when SLACK_WEBHOOKS is missing", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
123
|
+
return __generator(this, function (_a) {
|
|
124
|
+
switch (_a.label) {
|
|
125
|
+
case 0: return [4 /*yield*/, (0, slackNotifier_1.sendToSlack)("hello")];
|
|
126
|
+
case 1:
|
|
127
|
+
_a.sent();
|
|
128
|
+
(0, bun_test_1.expect)(mockAxiosPost.mock.calls.length).toBe(0);
|
|
129
|
+
return [2 /*return*/];
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}); });
|
|
133
|
+
(0, bun_test_1.it)("posts to default webhook with plain text", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
134
|
+
var callArgs, _a, url, payload;
|
|
135
|
+
return __generator(this, function (_b) {
|
|
136
|
+
switch (_b.label) {
|
|
137
|
+
case 0:
|
|
138
|
+
process.env.SLACK_WEBHOOKS = JSON.stringify({ default: "https://slack.example/webhook" });
|
|
139
|
+
mockAxiosPost.mockResolvedValue({ status: 200 });
|
|
140
|
+
return [4 /*yield*/, (0, slackNotifier_1.sendToSlack)("hello world")];
|
|
141
|
+
case 1:
|
|
142
|
+
_b.sent();
|
|
143
|
+
(0, bun_test_1.expect)(mockAxiosPost.mock.calls.length).toBe(1);
|
|
144
|
+
callArgs = mockAxiosPost.mock.calls[0];
|
|
145
|
+
(0, bun_test_1.expect)(Array.isArray(callArgs)).toBe(true);
|
|
146
|
+
_a = __read(callArgs, 2), url = _a[0], payload = _a[1];
|
|
147
|
+
(0, bun_test_1.expect)(url).toBe("https://slack.example/webhook");
|
|
148
|
+
(0, bun_test_1.expect)(payload).toEqual({ text: "hello world" });
|
|
149
|
+
return [2 /*return*/];
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}); });
|
|
153
|
+
(0, bun_test_1.it)("posts to a specific channel when provided", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
154
|
+
var callArgs, _a, url, payload;
|
|
155
|
+
return __generator(this, function (_b) {
|
|
156
|
+
switch (_b.label) {
|
|
157
|
+
case 0:
|
|
158
|
+
process.env.SLACK_WEBHOOKS = JSON.stringify({
|
|
159
|
+
default: "https://slack.example/default",
|
|
160
|
+
ops: "https://slack.example/ops",
|
|
161
|
+
});
|
|
162
|
+
mockAxiosPost.mockResolvedValue({ status: 200 });
|
|
163
|
+
return [4 /*yield*/, (0, slackNotifier_1.sendToSlack)("ops msg", { slackChannel: "ops" })];
|
|
164
|
+
case 1:
|
|
165
|
+
_b.sent();
|
|
166
|
+
callArgs = mockAxiosPost.mock.calls[0];
|
|
167
|
+
(0, bun_test_1.expect)(Array.isArray(callArgs)).toBe(true);
|
|
168
|
+
_a = __read(callArgs, 2), url = _a[0], payload = _a[1];
|
|
169
|
+
(0, bun_test_1.expect)(url).toBe("https://slack.example/ops");
|
|
170
|
+
(0, bun_test_1.expect)(payload).toEqual({ text: "ops msg" });
|
|
171
|
+
return [2 /*return*/];
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}); });
|
|
175
|
+
(0, bun_test_1.it)("falls back to default when channel not found", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
176
|
+
var callArgs, _a, url, payload;
|
|
177
|
+
return __generator(this, function (_b) {
|
|
178
|
+
switch (_b.label) {
|
|
179
|
+
case 0:
|
|
180
|
+
process.env.SLACK_WEBHOOKS = JSON.stringify({
|
|
181
|
+
default: "https://slack.example/default",
|
|
182
|
+
});
|
|
183
|
+
mockAxiosPost.mockResolvedValue({ status: 200 });
|
|
184
|
+
return [4 /*yield*/, (0, slackNotifier_1.sendToSlack)("missing channel", { slackChannel: "unknown" })];
|
|
185
|
+
case 1:
|
|
186
|
+
_b.sent();
|
|
187
|
+
callArgs = mockAxiosPost.mock.calls[0];
|
|
188
|
+
(0, bun_test_1.expect)(Array.isArray(callArgs)).toBe(true);
|
|
189
|
+
_a = __read(callArgs, 2), url = _a[0], payload = _a[1];
|
|
190
|
+
(0, bun_test_1.expect)(url).toBe("https://slack.example/default");
|
|
191
|
+
(0, bun_test_1.expect)(payload).toEqual({ text: "missing channel" });
|
|
192
|
+
return [2 /*return*/];
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}); });
|
|
196
|
+
(0, bun_test_1.it)("prefixes message with [ENV] when env provided", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
197
|
+
var callArgs, _a, payload;
|
|
198
|
+
return __generator(this, function (_b) {
|
|
199
|
+
switch (_b.label) {
|
|
200
|
+
case 0:
|
|
201
|
+
process.env.SLACK_WEBHOOKS = JSON.stringify({
|
|
202
|
+
default: "https://slack.example/default",
|
|
203
|
+
});
|
|
204
|
+
mockAxiosPost.mockResolvedValue({ status: 200 });
|
|
205
|
+
return [4 /*yield*/, (0, slackNotifier_1.sendToSlack)("status ok", { env: "stg" })];
|
|
206
|
+
case 1:
|
|
207
|
+
_b.sent();
|
|
208
|
+
callArgs = mockAxiosPost.mock.calls[0];
|
|
209
|
+
(0, bun_test_1.expect)(Array.isArray(callArgs)).toBe(true);
|
|
210
|
+
_a = __read(callArgs, 2), payload = _a[1];
|
|
211
|
+
(0, bun_test_1.expect)(payload).toEqual({ text: "[STG] status ok" });
|
|
212
|
+
return [2 /*return*/];
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}); });
|
|
216
|
+
(0, bun_test_1.it)("captures error and throws APIError when shouldThrow=true", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
217
|
+
var error_1;
|
|
218
|
+
return __generator(this, function (_a) {
|
|
219
|
+
switch (_a.label) {
|
|
220
|
+
case 0:
|
|
221
|
+
process.env.SLACK_WEBHOOKS = JSON.stringify({
|
|
222
|
+
default: "https://slack.example/default",
|
|
223
|
+
});
|
|
224
|
+
mockAxiosPost.mockRejectedValue(new Error("slack down"));
|
|
225
|
+
_a.label = 1;
|
|
226
|
+
case 1:
|
|
227
|
+
_a.trys.push([1, 3, , 4]);
|
|
228
|
+
return [4 /*yield*/, (0, slackNotifier_1.sendToSlack)("err", { shouldThrow: true })];
|
|
229
|
+
case 2:
|
|
230
|
+
_a.sent();
|
|
231
|
+
throw new Error("Expected sendToSlack to throw APIError");
|
|
232
|
+
case 3:
|
|
233
|
+
error_1 = _a.sent();
|
|
234
|
+
(0, bun_test_1.expect)(error_1.name).toBe("APIError");
|
|
235
|
+
(0, bun_test_1.expect)(error_1.title).toMatch(/Error posting to slack/i);
|
|
236
|
+
return [3 /*break*/, 4];
|
|
237
|
+
case 4:
|
|
238
|
+
(0, bun_test_1.expect)(mockAxiosPost.mock.calls.length).toBe(1);
|
|
239
|
+
return [2 /*return*/];
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}); });
|
|
243
|
+
(0, bun_test_1.it)("captures error and does not throw when shouldThrow=false", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
244
|
+
return __generator(this, function (_a) {
|
|
245
|
+
switch (_a.label) {
|
|
246
|
+
case 0:
|
|
247
|
+
process.env.SLACK_WEBHOOKS = JSON.stringify({
|
|
248
|
+
default: "https://slack.example/default",
|
|
249
|
+
});
|
|
250
|
+
mockAxiosPost.mockRejectedValue(new Error("slack intermittent"));
|
|
251
|
+
return [4 /*yield*/, (0, slackNotifier_1.sendToSlack)("err", { shouldThrow: false })];
|
|
252
|
+
case 1:
|
|
253
|
+
_a.sent();
|
|
254
|
+
(0, bun_test_1.expect)(mockAxiosPost.mock.calls.length).toBe(1);
|
|
255
|
+
return [2 /*return*/];
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}); });
|
|
259
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sends a rich formatted message to a Zoom chat channel via webhook.
|
|
3
|
+
*
|
|
4
|
+
* @param message - Message content with header, body, and optional subheader
|
|
5
|
+
* @param message.header - Main header text for the message
|
|
6
|
+
* @param message.body - Body text content
|
|
7
|
+
* @param message.subheader - Optional subheader text displayed below the main header
|
|
8
|
+
* @param options - Configuration options
|
|
9
|
+
* @param options.channel - The Zoom channel to post to (defaults to "default")
|
|
10
|
+
* @param options.shouldThrow - If true, throws an APIError on failure; otherwise logs and continues
|
|
11
|
+
* @param options.env - Optional environment prefix (e.g., "stg", "prod") prepended to header
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* Requires ZOOM_CHAT_WEBHOOKS environment variable containing JSON with channel configurations:
|
|
15
|
+
* ```json
|
|
16
|
+
* {
|
|
17
|
+
* "default": {"channel": "webhook_url", "verificationToken": "token"},
|
|
18
|
+
* "ops": {"channel": "webhook_url", "verificationToken": "token"}
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Falls back to "default" channel if specified channel not found.
|
|
23
|
+
* Logs errors to Sentry and logger when webhook is missing or request fails.
|
|
24
|
+
* Uses Zoom's rich message format (format=full) with structured header and body.
|
|
25
|
+
*/
|
|
26
|
+
export declare function sendToZoom({ header, body, subheader }: {
|
|
27
|
+
header: string;
|
|
28
|
+
body: string;
|
|
29
|
+
subheader?: string;
|
|
30
|
+
}, { channel, shouldThrow, env }: {
|
|
31
|
+
channel: string;
|
|
32
|
+
shouldThrow?: boolean;
|
|
33
|
+
env?: string;
|
|
34
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
45
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
46
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
47
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
48
|
+
function step(op) {
|
|
49
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
50
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
51
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
52
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
53
|
+
switch (op[0]) {
|
|
54
|
+
case 0: case 1: t = op; break;
|
|
55
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
56
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
57
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
58
|
+
default:
|
|
59
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
60
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
61
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
62
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
63
|
+
if (t[2]) _.ops.pop();
|
|
64
|
+
_.trys.pop(); continue;
|
|
65
|
+
}
|
|
66
|
+
op = body.call(thisArg, _);
|
|
67
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
68
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
72
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
73
|
+
};
|
|
74
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
75
|
+
exports.sendToZoom = sendToZoom;
|
|
76
|
+
var Sentry = __importStar(require("@sentry/node"));
|
|
77
|
+
var axios_1 = __importDefault(require("axios"));
|
|
78
|
+
var errors_1 = require("../errors");
|
|
79
|
+
var logger_1 = require("../logger");
|
|
80
|
+
/**
|
|
81
|
+
* Sends a rich formatted message to a Zoom chat channel via webhook.
|
|
82
|
+
*
|
|
83
|
+
* @param message - Message content with header, body, and optional subheader
|
|
84
|
+
* @param message.header - Main header text for the message
|
|
85
|
+
* @param message.body - Body text content
|
|
86
|
+
* @param message.subheader - Optional subheader text displayed below the main header
|
|
87
|
+
* @param options - Configuration options
|
|
88
|
+
* @param options.channel - The Zoom channel to post to (defaults to "default")
|
|
89
|
+
* @param options.shouldThrow - If true, throws an APIError on failure; otherwise logs and continues
|
|
90
|
+
* @param options.env - Optional environment prefix (e.g., "stg", "prod") prepended to header
|
|
91
|
+
*
|
|
92
|
+
* @remarks
|
|
93
|
+
* Requires ZOOM_CHAT_WEBHOOKS environment variable containing JSON with channel configurations:
|
|
94
|
+
* ```json
|
|
95
|
+
* {
|
|
96
|
+
* "default": {"channel": "webhook_url", "verificationToken": "token"},
|
|
97
|
+
* "ops": {"channel": "webhook_url", "verificationToken": "token"}
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* Falls back to "default" channel if specified channel not found.
|
|
102
|
+
* Logs errors to Sentry and logger when webhook is missing or request fails.
|
|
103
|
+
* Uses Zoom's rich message format (format=full) with structured header and body.
|
|
104
|
+
*/
|
|
105
|
+
function sendToZoom(_a, _b) {
|
|
106
|
+
return __awaiter(this, arguments, void 0, function (_c, _d) {
|
|
107
|
+
var zoomWebhooksString, msg, zoomWebhooks, zoomChannel, zoomWebhookUrl, msg, zoomToken, msg, messageBody, error_1;
|
|
108
|
+
var _e, _f, _g, _h, _j, _k, _l, _m;
|
|
109
|
+
var header = _c.header, body = _c.body, subheader = _c.subheader;
|
|
110
|
+
var channel = _d.channel, _o = _d.shouldThrow, shouldThrow = _o === void 0 ? false : _o, env = _d.env;
|
|
111
|
+
return __generator(this, function (_p) {
|
|
112
|
+
switch (_p.label) {
|
|
113
|
+
case 0:
|
|
114
|
+
zoomWebhooksString = process.env.ZOOM_CHAT_WEBHOOKS;
|
|
115
|
+
if (!zoomWebhooksString) {
|
|
116
|
+
msg = "ZOOM_CHAT_WEBHOOKS not set. Zoom message not sent";
|
|
117
|
+
Sentry.captureException(new Error(msg));
|
|
118
|
+
logger_1.logger.error(msg);
|
|
119
|
+
return [2 /*return*/];
|
|
120
|
+
}
|
|
121
|
+
zoomWebhooks = JSON.parse(zoomWebhooksString !== null && zoomWebhooksString !== void 0 ? zoomWebhooksString : "{}");
|
|
122
|
+
zoomChannel = channel !== null && channel !== void 0 ? channel : "default";
|
|
123
|
+
zoomWebhookUrl = (_f = (_e = zoomWebhooks[zoomChannel]) === null || _e === void 0 ? void 0 : _e.channel) !== null && _f !== void 0 ? _f : (_g = zoomWebhooks.default) === null || _g === void 0 ? void 0 : _g.channel;
|
|
124
|
+
if (!zoomWebhookUrl) {
|
|
125
|
+
msg = "No webhook url set in env for ".concat(zoomChannel, ". Zoom message not sent");
|
|
126
|
+
Sentry.captureException(new Error(msg));
|
|
127
|
+
logger_1.logger.error(msg);
|
|
128
|
+
return [2 /*return*/];
|
|
129
|
+
}
|
|
130
|
+
zoomToken = (_j = (_h = zoomWebhooks[zoomChannel]) === null || _h === void 0 ? void 0 : _h.verificationToken) !== null && _j !== void 0 ? _j : (_k = zoomWebhooks.default) === null || _k === void 0 ? void 0 : _k.verificationToken;
|
|
131
|
+
if (!zoomToken) {
|
|
132
|
+
msg = "No verification token set in env for ".concat(zoomChannel, ". Zoom message not sent");
|
|
133
|
+
Sentry.captureException(new Error(msg));
|
|
134
|
+
logger_1.logger.error(msg);
|
|
135
|
+
return [2 /*return*/];
|
|
136
|
+
}
|
|
137
|
+
messageBody = {
|
|
138
|
+
body: [
|
|
139
|
+
{
|
|
140
|
+
text: body,
|
|
141
|
+
type: "message",
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
head: {
|
|
145
|
+
text: env ? "[".concat(env.toUpperCase(), "] ").concat(header) : header,
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
// Add subheader if provided
|
|
149
|
+
if (subheader) {
|
|
150
|
+
messageBody.head.sub_head = {
|
|
151
|
+
text: subheader,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
_p.label = 1;
|
|
155
|
+
case 1:
|
|
156
|
+
_p.trys.push([1, 3, , 4]);
|
|
157
|
+
return [4 /*yield*/, axios_1.default.post("".concat(zoomWebhookUrl, "?format=full"), { content: messageBody }, {
|
|
158
|
+
headers: {
|
|
159
|
+
Authorization: zoomToken,
|
|
160
|
+
"Content-Type": "application/json",
|
|
161
|
+
},
|
|
162
|
+
})];
|
|
163
|
+
case 2:
|
|
164
|
+
_p.sent();
|
|
165
|
+
return [3 /*break*/, 4];
|
|
166
|
+
case 3:
|
|
167
|
+
error_1 = _p.sent();
|
|
168
|
+
logger_1.logger.error("Error posting to Zoom: ".concat((_l = error_1.text) !== null && _l !== void 0 ? _l : error_1.message));
|
|
169
|
+
Sentry.captureException(error_1);
|
|
170
|
+
if (shouldThrow) {
|
|
171
|
+
throw new errors_1.APIError({
|
|
172
|
+
status: 500,
|
|
173
|
+
title: "Error posting to Zoom: ".concat((_m = error_1.text) !== null && _m !== void 0 ? _m : error_1.message),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return [3 /*break*/, 4];
|
|
177
|
+
case 4: return [2 /*return*/];
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|