@terreno/api 0.13.2 → 0.14.0
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/dist/__tests__/versionCheckPlugin.test.js +53 -3
- package/dist/api.arrayOperations.test.js +1 -0
- package/dist/api.asyncHandler.test.d.ts +1 -0
- package/dist/api.asyncHandler.test.js +236 -0
- package/dist/api.d.ts +15 -4
- package/dist/api.errors.test.js +1 -0
- package/dist/api.hooks.test.js +1 -0
- package/dist/api.js +153 -104
- package/dist/api.query.test.js +1 -0
- package/dist/api.test.js +174 -0
- package/dist/auth.d.ts +10 -5
- package/dist/auth.js +163 -90
- package/dist/auth.test.js +159 -0
- package/dist/betterAuthApp.test.js +1 -0
- package/dist/betterAuthSetup.d.ts +5 -6
- package/dist/betterAuthSetup.js +17 -14
- package/dist/betterAuthSetup.test.js +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.js +248 -0
- package/dist/config.test.d.ts +1 -0
- package/dist/config.test.js +328 -0
- package/dist/configuration.test.js +1 -0
- package/dist/configurationApp.d.ts +1 -1
- package/dist/configurationApp.js +17 -13
- package/dist/configurationPlugin.test.js +1 -0
- package/dist/consentApp.test.js +1 -0
- package/dist/envConfigurationPlugin.d.ts +2 -0
- package/dist/envConfigurationPlugin.js +173 -0
- package/dist/envConfigurationPlugin.test.d.ts +1 -0
- package/dist/envConfigurationPlugin.test.js +322 -0
- package/dist/errors.d.ts +18 -7
- package/dist/errors.js +106 -10
- package/dist/errors.test.js +16 -1
- package/dist/example.js +16 -7
- package/dist/expressServer.d.ts +10 -9
- package/dist/expressServer.js +62 -53
- package/dist/expressServer.test.js +53 -2
- package/dist/githubAuth.d.ts +2 -1
- package/dist/githubAuth.js +41 -26
- package/dist/githubAuth.test.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +42 -20
- package/dist/models/versionConfig.d.ts +2 -0
- package/dist/models/versionConfig.js +8 -0
- package/dist/notifiers/googleChatNotifier.js +14 -16
- package/dist/notifiers/googleChatNotifier.test.js +1 -0
- package/dist/notifiers/slackNotifier.js +16 -14
- package/dist/notifiers/slackNotifier.test.js +41 -3
- package/dist/notifiers/zoomNotifier.js +7 -10
- package/dist/notifiers/zoomNotifier.test.js +1 -0
- package/dist/openApi.d.ts +1 -1
- package/dist/openApi.test.js +1 -0
- package/dist/openApiBuilder.d.ts +39 -6
- package/dist/openApiBuilder.js +1 -31
- package/dist/openApiBuilder.test.js +1 -0
- package/dist/openApiValidator.js +1 -0
- package/dist/openApiValidator.test.js +65 -0
- package/dist/permissions.d.ts +4 -4
- package/dist/permissions.js +67 -65
- package/dist/permissions.middleware.test.js +1 -0
- package/dist/permissions.test.js +1 -0
- package/dist/plugins.d.ts +5 -5
- package/dist/plugins.js +18 -9
- package/dist/plugins.test.js +1 -1
- package/dist/populate.d.ts +15 -8
- package/dist/populate.js +23 -24
- package/dist/populate.test.js +1 -0
- package/dist/realtime/changeStreamWatcher.d.ts +73 -0
- package/dist/realtime/changeStreamWatcher.js +720 -0
- package/dist/realtime/index.d.ts +6 -0
- package/dist/realtime/index.js +27 -0
- package/dist/realtime/queryMatcher.d.ts +14 -0
- package/dist/realtime/queryMatcher.js +250 -0
- package/dist/realtime/queryStore.d.ts +37 -0
- package/dist/realtime/queryStore.js +195 -0
- package/dist/realtime/realtime.test.d.ts +10 -0
- package/dist/realtime/realtime.test.js +2158 -0
- package/dist/realtime/realtimeApp.d.ts +93 -0
- package/dist/realtime/realtimeApp.js +560 -0
- package/dist/realtime/registry.d.ts +40 -0
- package/dist/realtime/registry.js +38 -0
- package/dist/realtime/socketUser.d.ts +10 -0
- package/dist/realtime/socketUser.js +17 -0
- package/dist/realtime/types.d.ts +100 -0
- package/dist/realtime/types.js +2 -0
- package/dist/requestContext.d.ts +37 -0
- package/dist/requestContext.js +344 -0
- package/dist/requestContext.test.d.ts +1 -0
- package/dist/requestContext.test.js +241 -0
- package/dist/terrenoApp.d.ts +8 -0
- package/dist/terrenoApp.js +50 -13
- package/dist/terrenoApp.test.js +194 -21
- package/dist/terrenoPlugin.d.ts +11 -0
- package/dist/tests/bunSetup.js +1 -0
- package/dist/tests.js +1 -1
- package/dist/transformers.d.ts +2 -2
- package/dist/transformers.js +5 -3
- package/dist/transformers.test.js +90 -0
- package/dist/types/consentResponse.d.ts +6 -3
- package/dist/versionCheckPlugin.d.ts +2 -0
- package/dist/versionCheckPlugin.js +18 -12
- package/package.json +4 -2
- package/src/__tests__/versionCheckPlugin.test.ts +37 -3
- package/src/api.arrayOperations.test.ts +1 -0
- package/src/api.asyncHandler.test.ts +177 -0
- package/src/api.errors.test.ts +1 -0
- package/src/api.hooks.test.ts +1 -0
- package/src/api.query.test.ts +1 -0
- package/src/api.test.ts +132 -0
- package/src/api.ts +199 -84
- package/src/auth.test.ts +160 -0
- package/src/auth.ts +120 -50
- package/src/betterAuthApp.test.ts +1 -0
- package/src/betterAuthSetup.test.ts +1 -0
- package/src/betterAuthSetup.ts +46 -19
- package/src/config.test.ts +255 -0
- package/src/config.ts +206 -0
- package/src/configuration.test.ts +1 -0
- package/src/configurationApp.ts +59 -24
- package/src/configurationPlugin.test.ts +1 -0
- package/src/consentApp.test.ts +1 -0
- package/src/envConfigurationPlugin.test.ts +143 -0
- package/src/envConfigurationPlugin.ts +100 -0
- package/src/errors.test.ts +19 -1
- package/src/errors.ts +94 -20
- package/src/example.ts +46 -21
- package/src/express.d.ts +18 -1
- package/src/expressServer.test.ts +50 -2
- package/src/expressServer.ts +80 -50
- package/src/githubAuth.test.ts +1 -0
- package/src/githubAuth.ts +59 -38
- package/src/index.ts +4 -0
- package/src/logger.ts +47 -17
- package/src/models/versionConfig.ts +13 -2
- package/src/notifiers/googleChatNotifier.test.ts +1 -0
- package/src/notifiers/googleChatNotifier.ts +7 -9
- package/src/notifiers/slackNotifier.test.ts +29 -3
- package/src/notifiers/slackNotifier.ts +9 -7
- package/src/notifiers/zoomNotifier.test.ts +1 -0
- package/src/notifiers/zoomNotifier.ts +8 -11
- package/src/openApi.test.ts +1 -0
- package/src/openApi.ts +4 -4
- package/src/openApiBuilder.test.ts +1 -0
- package/src/openApiBuilder.ts +14 -11
- package/src/openApiValidator.test.ts +59 -0
- package/src/openApiValidator.ts +3 -2
- package/src/permissions.middleware.test.ts +1 -0
- package/src/permissions.test.ts +1 -0
- package/src/permissions.ts +30 -25
- package/src/plugins.test.ts +1 -1
- package/src/plugins.ts +21 -14
- package/src/populate.test.ts +1 -0
- package/src/populate.ts +44 -36
- package/src/realtime/changeStreamWatcher.ts +568 -0
- package/src/realtime/index.ts +34 -0
- package/src/realtime/queryMatcher.ts +179 -0
- package/src/realtime/queryStore.ts +132 -0
- package/src/realtime/realtime.test.ts +1755 -0
- package/src/realtime/realtimeApp.ts +478 -0
- package/src/realtime/registry.ts +64 -0
- package/src/realtime/socketUser.ts +25 -0
- package/src/realtime/types.ts +112 -0
- package/src/requestContext.test.ts +196 -0
- package/src/requestContext.ts +368 -0
- package/src/terrenoApp.test.ts +137 -11
- package/src/terrenoApp.ts +64 -17
- package/src/terrenoPlugin.ts +12 -0
- package/src/tests/bunSetup.ts +1 -0
- package/src/tests.ts +7 -2
- package/src/transformers.test.ts +70 -2
- package/src/transformers.ts +15 -7
- package/src/types/consentResponse.ts +8 -10
- package/src/versionCheckPlugin.ts +15 -7
package/dist/logger.js
CHANGED
|
@@ -83,50 +83,70 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
83
83
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
84
84
|
};
|
|
85
85
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
86
|
-
exports.logger = exports.winstonLogger = void 0;
|
|
87
|
-
exports.setupLogging = setupLogging;
|
|
86
|
+
exports.setupLogging = exports.logger = exports.winstonLogger = void 0;
|
|
88
87
|
var node_fs_1 = __importDefault(require("node:fs"));
|
|
89
88
|
var node_util_1 = require("node:util");
|
|
90
89
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
91
90
|
var winston_1 = __importDefault(require("winston"));
|
|
92
|
-
|
|
91
|
+
var requestContext_1 = require("./requestContext");
|
|
92
|
+
var isPrimitive = function (val) {
|
|
93
93
|
return val === null || (typeof val !== "object" && typeof val !== "function");
|
|
94
|
-
}
|
|
95
|
-
function
|
|
94
|
+
};
|
|
95
|
+
var formatWithInspect = function (val) {
|
|
96
96
|
var prefix = isPrimitive(val) ? "" : "\n";
|
|
97
97
|
var shouldFormat = typeof val !== "string";
|
|
98
98
|
return prefix + (shouldFormat ? (0, node_util_1.inspect)(val, { colors: true, depth: null }) : val);
|
|
99
|
-
}
|
|
99
|
+
};
|
|
100
|
+
var addRequestContextFormat = winston_1.default.format(function (info) {
|
|
101
|
+
var context = (0, requestContext_1.getCurrentLogContext)();
|
|
102
|
+
return __assign(__assign({}, context), info);
|
|
103
|
+
});
|
|
104
|
+
var formatContext = function (info) {
|
|
105
|
+
var contextParts = [
|
|
106
|
+
info.requestId ? "requestId=".concat(info.requestId) : undefined,
|
|
107
|
+
info.jobId ? "jobId=".concat(info.jobId) : undefined,
|
|
108
|
+
info.sessionId ? "sessionId=".concat(info.sessionId) : undefined,
|
|
109
|
+
info.userId ? "userId=".concat(info.userId) : undefined,
|
|
110
|
+
info.traceId ? "traceId=".concat(info.traceId) : undefined,
|
|
111
|
+
].filter(Boolean);
|
|
112
|
+
if (contextParts.length === 0) {
|
|
113
|
+
return "";
|
|
114
|
+
}
|
|
115
|
+
return " ".concat(contextParts.join(" "));
|
|
116
|
+
};
|
|
100
117
|
// Winston doesn't operate like console.log by default, e.g. `logger.error('error',
|
|
101
118
|
// error)` only prints the message and no args. Add handling for all the args,
|
|
102
119
|
// while also supporting splat logging.
|
|
103
|
-
function
|
|
120
|
+
var printf = function (timestamp) {
|
|
104
121
|
if (timestamp === void 0) { timestamp = false; }
|
|
105
122
|
return function (info) {
|
|
106
123
|
var msg = formatWithInspect(info.message);
|
|
107
|
-
var
|
|
124
|
+
var splatKey = Symbol.for("splat");
|
|
125
|
+
var splatArgs = (info[splatKey] || []);
|
|
108
126
|
var rest = splatArgs.map(function (data) { return formatWithInspect(data); }).join(" ");
|
|
127
|
+
var context = formatContext(info);
|
|
109
128
|
if (timestamp) {
|
|
110
|
-
return "".concat(info.timestamp, " - ").concat(info.level, ": ").concat(msg, " ").concat(rest);
|
|
129
|
+
return "".concat(info.timestamp, " - ").concat(info.level, ": ").concat(msg).concat(context, " ").concat(rest);
|
|
111
130
|
}
|
|
112
|
-
return "".concat(info.level, ": ").concat(msg, " ").concat(rest);
|
|
131
|
+
return "".concat(info.level, ": ").concat(msg).concat(context, " ").concat(rest);
|
|
113
132
|
};
|
|
114
|
-
}
|
|
133
|
+
};
|
|
115
134
|
// Setup a global, default rejection handler.
|
|
116
135
|
winston_1.default.add(new winston_1.default.transports.Console({
|
|
117
136
|
debugStdout: true,
|
|
118
|
-
format: winston_1.default.format.combine(winston_1.default.format.colorize(), winston_1.default.format.simple(), winston_1.default.format.printf(printf(false))),
|
|
137
|
+
format: winston_1.default.format.combine(addRequestContextFormat(), winston_1.default.format.colorize(), winston_1.default.format.simple(), winston_1.default.format.printf(printf(false))),
|
|
119
138
|
handleExceptions: true,
|
|
120
139
|
handleRejections: true,
|
|
121
140
|
level: "error",
|
|
122
141
|
}));
|
|
123
142
|
// Setup a default console logger.
|
|
124
143
|
exports.winstonLogger = winston_1.default.createLogger({
|
|
144
|
+
format: addRequestContextFormat(),
|
|
125
145
|
level: "debug",
|
|
126
146
|
transports: [
|
|
127
147
|
new winston_1.default.transports.Console({
|
|
128
148
|
debugStdout: true,
|
|
129
|
-
format: winston_1.default.format.combine(winston_1.default.format.colorize(), winston_1.default.format.simple(), winston_1.default.format.printf(printf(false))),
|
|
149
|
+
format: winston_1.default.format.combine(addRequestContextFormat(), winston_1.default.format.colorize(), winston_1.default.format.simple(), winston_1.default.format.printf(printf(false))),
|
|
130
150
|
handleExceptions: true,
|
|
131
151
|
handleRejections: true,
|
|
132
152
|
level: "debug",
|
|
@@ -134,11 +154,12 @@ exports.winstonLogger = winston_1.default.createLogger({
|
|
|
134
154
|
],
|
|
135
155
|
});
|
|
136
156
|
// Helper function to send logs to Sentry if enabled
|
|
137
|
-
function
|
|
157
|
+
var sendToSentry = function (message, level) {
|
|
138
158
|
if (process.env.USE_SENTRY_LOGGING === "true" && Sentry.logger) {
|
|
139
|
-
Sentry.logger[level]
|
|
159
|
+
var logWithContext = Sentry.logger[level];
|
|
160
|
+
logWithContext(message, (0, requestContext_1.getCurrentLogContext)());
|
|
140
161
|
}
|
|
141
|
-
}
|
|
162
|
+
};
|
|
142
163
|
exports.logger = {
|
|
143
164
|
// simple way to log a caught exception. e.g. promise().catch(logger.catch)
|
|
144
165
|
catch: function (e) {
|
|
@@ -186,12 +207,12 @@ exports.logger = {
|
|
|
186
207
|
sendToSentry(msg, "warn");
|
|
187
208
|
},
|
|
188
209
|
};
|
|
189
|
-
function
|
|
210
|
+
var setupLogging = function (options) {
|
|
190
211
|
var _a, e_1, _b;
|
|
191
212
|
var _c, _d;
|
|
192
213
|
exports.winstonLogger.clear();
|
|
193
214
|
if (!(options === null || options === void 0 ? void 0 : options.disableConsoleLogging)) {
|
|
194
|
-
var formats = [winston_1.default.format.simple()];
|
|
215
|
+
var formats = [addRequestContextFormat(), winston_1.default.format.simple()];
|
|
195
216
|
if (!(options === null || options === void 0 ? void 0 : options.disableConsoleColors)) {
|
|
196
217
|
formats.push(winston_1.default.format.colorize());
|
|
197
218
|
}
|
|
@@ -211,7 +232,7 @@ function setupLogging(options) {
|
|
|
211
232
|
colorize: false,
|
|
212
233
|
compress: true,
|
|
213
234
|
dirname: logDirectory,
|
|
214
|
-
format: winston_1.default.format.simple(),
|
|
235
|
+
format: winston_1.default.format.combine(addRequestContextFormat(), winston_1.default.format.simple()),
|
|
215
236
|
// 30 days of retention
|
|
216
237
|
maxFiles: 30,
|
|
217
238
|
// 50MB max file size
|
|
@@ -246,4 +267,5 @@ function setupLogging(options) {
|
|
|
246
267
|
finally { if (e_1) throw e_1.error; }
|
|
247
268
|
}
|
|
248
269
|
}
|
|
249
|
-
}
|
|
270
|
+
};
|
|
271
|
+
exports.setupLogging = setupLogging;
|
|
@@ -8,6 +8,8 @@ export interface VersionConfigDocument extends mongoose.Document {
|
|
|
8
8
|
warningMessage: string;
|
|
9
9
|
requiredMessage: string;
|
|
10
10
|
updateUrl?: string;
|
|
11
|
+
/** How often clients should poll for version updates, in minutes. Defaults to 1440 (24 hours). */
|
|
12
|
+
pollingIntervalMinutes: number;
|
|
11
13
|
created?: Date;
|
|
12
14
|
updated?: Date;
|
|
13
15
|
}
|
|
@@ -19,6 +19,12 @@ var versionConfigSchema = new mongoose_1.default.Schema({
|
|
|
19
19
|
min: 0,
|
|
20
20
|
type: Number,
|
|
21
21
|
},
|
|
22
|
+
pollingIntervalMinutes: {
|
|
23
|
+
default: 1440,
|
|
24
|
+
description: "How often clients poll for version updates, in minutes (default: 1440 = 24 hours)",
|
|
25
|
+
min: 1,
|
|
26
|
+
type: Number,
|
|
27
|
+
},
|
|
22
28
|
requiredMessage: {
|
|
23
29
|
default: "This version is no longer supported. Please update to continue.",
|
|
24
30
|
description: "Message shown on the blocking screen",
|
|
@@ -62,5 +68,7 @@ versionConfigSchema.add({
|
|
|
62
68
|
});
|
|
63
69
|
versionConfigSchema.index({ _singleton: 1 }, { unique: true });
|
|
64
70
|
versionConfigSchema.plugin(plugins_1.createdUpdatedPlugin);
|
|
71
|
+
versionConfigSchema.plugin(plugins_1.isDeletedPlugin);
|
|
65
72
|
versionConfigSchema.plugin(plugins_1.findOneOrNone);
|
|
73
|
+
versionConfigSchema.plugin(plugins_1.findExactlyOne);
|
|
66
74
|
exports.VersionConfig = mongoose_1.default.model("VersionConfig", versionConfigSchema);
|
|
@@ -108,17 +108,16 @@ var sendToGoogleChat = function (messageText_1) {
|
|
|
108
108
|
args_1[_i - 1] = arguments[_i];
|
|
109
109
|
}
|
|
110
110
|
return __awaiter(void 0, __spreadArray([messageText_1], __read(args_1), false), void 0, function (messageText, _a) {
|
|
111
|
-
var chatWebhooksString, msg, chatWebhooks, chatChannel, chatWebhookUrl, msg, formattedMessageText, error_1,
|
|
112
|
-
var _b
|
|
113
|
-
var
|
|
114
|
-
return __generator(this, function (
|
|
115
|
-
switch (
|
|
111
|
+
var chatWebhooksString, msg, chatWebhooks, chatChannel, chatWebhookUrl, msg, formattedMessageText, error_1, message;
|
|
112
|
+
var _b;
|
|
113
|
+
var _c = _a === void 0 ? {} : _a, channel = _c.channel, _d = _c.shouldThrow, shouldThrow = _d === void 0 ? false : _d, env = _c.env;
|
|
114
|
+
return __generator(this, function (_e) {
|
|
115
|
+
switch (_e.label) {
|
|
116
116
|
case 0:
|
|
117
117
|
chatWebhooksString = process.env.GOOGLE_CHAT_WEBHOOKS;
|
|
118
118
|
if (!chatWebhooksString) {
|
|
119
119
|
msg = "GOOGLE_CHAT_WEBHOOKS not set. Google Chat message not sent";
|
|
120
|
-
Sentry.captureException(new
|
|
121
|
-
logger_1.logger.error(msg);
|
|
120
|
+
Sentry.captureException(new errors_1.APIError({ status: 500, title: msg }));
|
|
122
121
|
return [2 /*return*/];
|
|
123
122
|
}
|
|
124
123
|
chatWebhooks = JSON.parse(chatWebhooksString !== null && chatWebhooksString !== void 0 ? chatWebhooksString : "{}");
|
|
@@ -126,30 +125,29 @@ var sendToGoogleChat = function (messageText_1) {
|
|
|
126
125
|
chatWebhookUrl = (_b = chatWebhooks[chatChannel]) !== null && _b !== void 0 ? _b : chatWebhooks.default;
|
|
127
126
|
if (!chatWebhookUrl) {
|
|
128
127
|
msg = "No webhook url set in env for ".concat(chatChannel, ". Google Chat message not sent");
|
|
129
|
-
Sentry.captureException(new
|
|
130
|
-
logger_1.logger.error(msg);
|
|
128
|
+
Sentry.captureException(new errors_1.APIError({ status: 500, title: msg }));
|
|
131
129
|
return [2 /*return*/];
|
|
132
130
|
}
|
|
133
131
|
formattedMessageText = messageText;
|
|
134
132
|
if (env) {
|
|
135
133
|
formattedMessageText = "[".concat(env.toUpperCase(), "] ").concat(messageText);
|
|
136
134
|
}
|
|
137
|
-
|
|
135
|
+
_e.label = 1;
|
|
138
136
|
case 1:
|
|
139
|
-
|
|
137
|
+
_e.trys.push([1, 3, , 4]);
|
|
140
138
|
return [4 /*yield*/, axios_1.default.post(chatWebhookUrl, { text: formattedMessageText })];
|
|
141
139
|
case 2:
|
|
142
|
-
|
|
140
|
+
_e.sent();
|
|
143
141
|
return [3 /*break*/, 4];
|
|
144
142
|
case 3:
|
|
145
|
-
error_1 =
|
|
146
|
-
|
|
147
|
-
logger_1.logger.error("Error posting to Google Chat: ".concat(
|
|
143
|
+
error_1 = _e.sent();
|
|
144
|
+
message = (0, errors_1.errorMessage)(error_1);
|
|
145
|
+
logger_1.logger.error("Error posting to Google Chat: ".concat(message));
|
|
148
146
|
Sentry.captureException(error_1);
|
|
149
147
|
if (shouldThrow) {
|
|
150
148
|
throw new errors_1.APIError({
|
|
151
149
|
status: 500,
|
|
152
|
-
title: "Error posting to Google Chat: ".concat(
|
|
150
|
+
title: "Error posting to Google Chat: ".concat(message),
|
|
153
151
|
});
|
|
154
152
|
}
|
|
155
153
|
return [3 /*break*/, 4];
|
|
@@ -99,6 +99,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
99
99
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
100
100
|
};
|
|
101
101
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
102
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
102
103
|
var bun_test_1 = require("bun:test");
|
|
103
104
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
104
105
|
var axios_1 = __importDefault(require("axios"));
|
|
@@ -112,11 +112,11 @@ var sendToSlack = function (text_1) {
|
|
|
112
112
|
args_1[_i - 1] = arguments[_i];
|
|
113
113
|
}
|
|
114
114
|
return __awaiter(void 0, __spreadArray([text_1], __read(args_1), false), void 0, function (text, _a) {
|
|
115
|
-
var slackWebhookUrl, slackWebhooksString, slackWebhooks, channel, formattedText, error_1,
|
|
116
|
-
var _b
|
|
117
|
-
var
|
|
118
|
-
return __generator(this, function (
|
|
119
|
-
switch (
|
|
115
|
+
var slackWebhookUrl, slackWebhooksString, slackWebhooks, channel, formattedText, error_1, message;
|
|
116
|
+
var _b;
|
|
117
|
+
var _c = _a === void 0 ? {} : _a, slackChannel = _c.slackChannel, _d = _c.shouldThrow, shouldThrow = _d === void 0 ? false : _d, env = _c.env, url = _c.url;
|
|
118
|
+
return __generator(this, function (_e) {
|
|
119
|
+
switch (_e.label) {
|
|
120
120
|
case 0:
|
|
121
121
|
slackWebhookUrl = url;
|
|
122
122
|
if (!slackWebhookUrl) {
|
|
@@ -130,8 +130,10 @@ var sendToSlack = function (text_1) {
|
|
|
130
130
|
channel = slackChannel !== null && slackChannel !== void 0 ? slackChannel : "default";
|
|
131
131
|
slackWebhookUrl = (_b = slackWebhooks[channel]) !== null && _b !== void 0 ? _b : slackWebhooks.default;
|
|
132
132
|
if (!slackWebhookUrl) {
|
|
133
|
-
Sentry.captureException(new
|
|
134
|
-
|
|
133
|
+
Sentry.captureException(new errors_1.APIError({
|
|
134
|
+
status: 500,
|
|
135
|
+
title: "No webhook url set in env for ".concat(channel, ". Slack message not sent"),
|
|
136
|
+
}));
|
|
135
137
|
return [2 /*return*/];
|
|
136
138
|
}
|
|
137
139
|
}
|
|
@@ -139,24 +141,24 @@ var sendToSlack = function (text_1) {
|
|
|
139
141
|
if (env) {
|
|
140
142
|
formattedText = "[".concat(env.toUpperCase(), "] ").concat(text);
|
|
141
143
|
}
|
|
142
|
-
|
|
144
|
+
_e.label = 1;
|
|
143
145
|
case 1:
|
|
144
|
-
|
|
146
|
+
_e.trys.push([1, 3, , 4]);
|
|
145
147
|
return [4 /*yield*/, axios_1.default.post(slackWebhookUrl, {
|
|
146
148
|
text: formattedText,
|
|
147
149
|
})];
|
|
148
150
|
case 2:
|
|
149
|
-
|
|
151
|
+
_e.sent();
|
|
150
152
|
return [3 /*break*/, 4];
|
|
151
153
|
case 3:
|
|
152
|
-
error_1 =
|
|
153
|
-
|
|
154
|
-
logger_1.logger.error("Error posting to slack: ".concat(
|
|
154
|
+
error_1 = _e.sent();
|
|
155
|
+
message = (0, errors_1.errorMessage)(error_1);
|
|
156
|
+
logger_1.logger.error("Error posting to slack: ".concat(message));
|
|
155
157
|
Sentry.captureException(error_1);
|
|
156
158
|
if (shouldThrow) {
|
|
157
159
|
throw new errors_1.APIError({
|
|
158
160
|
status: 500,
|
|
159
|
-
title: "Error posting to slack: ".concat(
|
|
161
|
+
title: "Error posting to slack: ".concat(message),
|
|
160
162
|
});
|
|
161
163
|
}
|
|
162
164
|
return [3 /*break*/, 4];
|
|
@@ -102,6 +102,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
102
102
|
var bun_test_1 = require("bun:test");
|
|
103
103
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
104
104
|
var axios_1 = __importDefault(require("axios"));
|
|
105
|
+
var errors_1 = require("../errors");
|
|
105
106
|
var slackNotifier_1 = require("./slackNotifier");
|
|
106
107
|
(0, bun_test_1.describe)("sendToSlack", function () {
|
|
107
108
|
var mockAxiosPost;
|
|
@@ -213,8 +214,44 @@ var slackNotifier_1 = require("./slackNotifier");
|
|
|
213
214
|
}
|
|
214
215
|
});
|
|
215
216
|
}); });
|
|
217
|
+
(0, bun_test_1.it)("reports to Sentry and returns early when channel has no webhook and no default", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
218
|
+
var captured;
|
|
219
|
+
return __generator(this, function (_a) {
|
|
220
|
+
switch (_a.label) {
|
|
221
|
+
case 0:
|
|
222
|
+
process.env.SLACK_WEBHOOKS = JSON.stringify({ ops: "https://slack.example/ops" });
|
|
223
|
+
return [4 /*yield*/, (0, slackNotifier_1.sendToSlack)("orphan message", { slackChannel: "alerts" })];
|
|
224
|
+
case 1:
|
|
225
|
+
_a.sent();
|
|
226
|
+
(0, bun_test_1.expect)(mockAxiosPost.mock.calls.length).toBe(0);
|
|
227
|
+
(0, bun_test_1.expect)(Sentry.captureException.mock.calls.length).toBe(1);
|
|
228
|
+
captured = Sentry.captureException.mock
|
|
229
|
+
.calls[0][0];
|
|
230
|
+
(0, bun_test_1.expect)(captured).toBeInstanceOf(errors_1.APIError);
|
|
231
|
+
(0, bun_test_1.expect)(captured.title).toContain("alerts");
|
|
232
|
+
return [2 /*return*/];
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}); });
|
|
236
|
+
(0, bun_test_1.it)("posts directly using the url parameter without env lookup", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
237
|
+
var _a, url, payload;
|
|
238
|
+
return __generator(this, function (_b) {
|
|
239
|
+
switch (_b.label) {
|
|
240
|
+
case 0:
|
|
241
|
+
mockAxiosPost.mockResolvedValue({ status: 200 });
|
|
242
|
+
return [4 /*yield*/, (0, slackNotifier_1.sendToSlack)("direct msg", { url: "https://direct.example/hook" })];
|
|
243
|
+
case 1:
|
|
244
|
+
_b.sent();
|
|
245
|
+
(0, bun_test_1.expect)(mockAxiosPost.mock.calls.length).toBe(1);
|
|
246
|
+
_a = __read(mockAxiosPost.mock.calls[0], 2), url = _a[0], payload = _a[1];
|
|
247
|
+
(0, bun_test_1.expect)(url).toBe("https://direct.example/hook");
|
|
248
|
+
(0, bun_test_1.expect)(payload).toEqual({ text: "direct msg" });
|
|
249
|
+
return [2 /*return*/];
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}); });
|
|
216
253
|
(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;
|
|
254
|
+
var error_1, apiError;
|
|
218
255
|
return __generator(this, function (_a) {
|
|
219
256
|
switch (_a.label) {
|
|
220
257
|
case 0:
|
|
@@ -231,8 +268,9 @@ var slackNotifier_1 = require("./slackNotifier");
|
|
|
231
268
|
throw new Error("Expected sendToSlack to throw APIError");
|
|
232
269
|
case 3:
|
|
233
270
|
error_1 = _a.sent();
|
|
234
|
-
|
|
235
|
-
(0, bun_test_1.expect)(
|
|
271
|
+
apiError = error_1;
|
|
272
|
+
(0, bun_test_1.expect)(apiError.name).toBe("APIError");
|
|
273
|
+
(0, bun_test_1.expect)(apiError.title).toMatch(/Error posting to slack/i);
|
|
236
274
|
return [3 /*break*/, 4];
|
|
237
275
|
case 4:
|
|
238
276
|
(0, bun_test_1.expect)(mockAxiosPost.mock.calls.length).toBe(1);
|
|
@@ -103,7 +103,7 @@ var logger_1 = require("../logger");
|
|
|
103
103
|
* Uses Zoom's rich message format (format=full) with structured header and body.
|
|
104
104
|
*/
|
|
105
105
|
var sendToZoom = function (_a, _b) { return __awaiter(void 0, [_a, _b], void 0, function (_c, _d) {
|
|
106
|
-
var zoomWebhooksString, msg, zoomWebhooks, zoomChannel, zoomWebhookUrl, msg, zoomToken, msg, messageBody, error_1,
|
|
106
|
+
var zoomWebhooksString, msg, zoomWebhooks, zoomChannel, zoomWebhookUrl, msg, zoomToken, msg, messageBody, error_1, message;
|
|
107
107
|
var _e, _f, _g, _h, _j, _k;
|
|
108
108
|
var header = _c.header, body = _c.body, subheader = _c.subheader;
|
|
109
109
|
var channel = _d.channel, _l = _d.shouldThrow, shouldThrow = _l === void 0 ? false : _l, env = _d.env;
|
|
@@ -113,8 +113,7 @@ var sendToZoom = function (_a, _b) { return __awaiter(void 0, [_a, _b], void 0,
|
|
|
113
113
|
zoomWebhooksString = process.env.ZOOM_CHAT_WEBHOOKS;
|
|
114
114
|
if (!zoomWebhooksString) {
|
|
115
115
|
msg = "ZOOM_CHAT_WEBHOOKS not set. Zoom message not sent";
|
|
116
|
-
Sentry.captureException(new
|
|
117
|
-
logger_1.logger.error(msg);
|
|
116
|
+
Sentry.captureException(new errors_1.APIError({ status: 500, title: msg }));
|
|
118
117
|
return [2 /*return*/];
|
|
119
118
|
}
|
|
120
119
|
zoomWebhooks = JSON.parse(zoomWebhooksString !== null && zoomWebhooksString !== void 0 ? zoomWebhooksString : "{}");
|
|
@@ -122,15 +121,13 @@ var sendToZoom = function (_a, _b) { return __awaiter(void 0, [_a, _b], void 0,
|
|
|
122
121
|
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;
|
|
123
122
|
if (!zoomWebhookUrl) {
|
|
124
123
|
msg = "No webhook url set in env for ".concat(zoomChannel, ". Zoom message not sent");
|
|
125
|
-
Sentry.captureException(new
|
|
126
|
-
logger_1.logger.error(msg);
|
|
124
|
+
Sentry.captureException(new errors_1.APIError({ status: 500, title: msg }));
|
|
127
125
|
return [2 /*return*/];
|
|
128
126
|
}
|
|
129
127
|
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;
|
|
130
128
|
if (!zoomToken) {
|
|
131
129
|
msg = "No verification token set in env for ".concat(zoomChannel, ". Zoom message not sent");
|
|
132
|
-
Sentry.captureException(new
|
|
133
|
-
logger_1.logger.error(msg);
|
|
130
|
+
Sentry.captureException(new errors_1.APIError({ status: 500, title: msg }));
|
|
134
131
|
return [2 /*return*/];
|
|
135
132
|
}
|
|
136
133
|
messageBody = {
|
|
@@ -163,13 +160,13 @@ var sendToZoom = function (_a, _b) { return __awaiter(void 0, [_a, _b], void 0,
|
|
|
163
160
|
return [3 /*break*/, 4];
|
|
164
161
|
case 3:
|
|
165
162
|
error_1 = _m.sent();
|
|
166
|
-
|
|
167
|
-
logger_1.logger.error("Error posting to Zoom: ".concat(
|
|
163
|
+
message = (0, errors_1.errorMessage)(error_1);
|
|
164
|
+
logger_1.logger.error("Error posting to Zoom: ".concat(message));
|
|
168
165
|
Sentry.captureException(error_1);
|
|
169
166
|
if (shouldThrow) {
|
|
170
167
|
throw new errors_1.APIError({
|
|
171
168
|
status: 500,
|
|
172
|
-
title: "Error posting to Zoom: ".concat(
|
|
169
|
+
title: "Error posting to Zoom: ".concat(message),
|
|
173
170
|
});
|
|
174
171
|
}
|
|
175
172
|
return [3 /*break*/, 4];
|
|
@@ -99,6 +99,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
99
99
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
100
100
|
};
|
|
101
101
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
102
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
102
103
|
var bun_test_1 = require("bun:test");
|
|
103
104
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
104
105
|
var axios_1 = __importDefault(require("axios"));
|
package/dist/openApi.d.ts
CHANGED
|
@@ -58,4 +58,4 @@ export declare function listOpenApiMiddleware<T>(model: Model<T>, options: Parti
|
|
|
58
58
|
export declare function createOpenApiMiddleware<T>(model: Model<T>, options: Partial<ModelRouterOptions<T>>): express.RequestHandler;
|
|
59
59
|
export declare function patchOpenApiMiddleware<T>(model: Model<T>, options: Partial<ModelRouterOptions<T>>): express.RequestHandler;
|
|
60
60
|
export declare function deleteOpenApiMiddleware<T>(model: Model<T>, options: Partial<ModelRouterOptions<T>>): express.RequestHandler;
|
|
61
|
-
export declare function readOpenApiMiddleware<T>(options: Partial<ModelRouterOptions<T>>, properties:
|
|
61
|
+
export declare function readOpenApiMiddleware<T>(options: Partial<ModelRouterOptions<T>>, properties: Record<string, unknown>, required: string[], queryParameters: Array<Record<string, unknown>>): express.RequestHandler;
|
package/dist/openApi.test.js
CHANGED
|
@@ -66,6 +66,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
66
66
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
67
67
|
};
|
|
68
68
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
69
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
69
70
|
var bun_test_1 = require("bun:test");
|
|
70
71
|
var supertest_1 = __importDefault(require("supertest"));
|
|
71
72
|
var api_1 = require("./api");
|
package/dist/openApiBuilder.d.ts
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAPI Middleware Builder
|
|
3
|
+
*
|
|
4
|
+
* This module provides a fluent builder pattern for constructing OpenAPI middleware
|
|
5
|
+
* for Express routes that don't directly map to Mongoose models. It allows you to
|
|
6
|
+
* define custom API documentation with full control over request/response schemas.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import {createOpenApiBuilder} from "./openApiBuilder";
|
|
13
|
+
*
|
|
14
|
+
* // Create middleware with custom documentation
|
|
15
|
+
* const middleware = createOpenApiBuilder(options)
|
|
16
|
+
* .withTags(["users"])
|
|
17
|
+
* .withSummary("Get user statistics")
|
|
18
|
+
* .withDescription("Returns aggregated statistics for the current user")
|
|
19
|
+
* .withQueryParameter("period", {type: "string"}, {
|
|
20
|
+
* description: "Time period for statistics",
|
|
21
|
+
* required: false,
|
|
22
|
+
* })
|
|
23
|
+
* .withResponse<{count: number; average: number}>(200, {
|
|
24
|
+
* count: {type: "number", description: "Total count"},
|
|
25
|
+
* average: {type: "number", description: "Average value"},
|
|
26
|
+
* })
|
|
27
|
+
* .build();
|
|
28
|
+
*
|
|
29
|
+
* router.get("/stats", middleware, statsHandler);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
import type express from "express";
|
|
1
33
|
import type { ModelRouterOptions } from "./api";
|
|
2
34
|
/**
|
|
3
35
|
* Defines a property within an OpenAPI schema.
|
|
@@ -68,7 +100,7 @@ export interface OpenApiSchemaProperty {
|
|
|
68
100
|
* };
|
|
69
101
|
* ```
|
|
70
102
|
*/
|
|
71
|
-
export
|
|
103
|
+
export interface OpenApiSchema {
|
|
72
104
|
/** The JSON Schema type (typically "object" or "array") */
|
|
73
105
|
type: string;
|
|
74
106
|
/** Property definitions for object types */
|
|
@@ -79,7 +111,8 @@ export type OpenApiSchema = {
|
|
|
79
111
|
items?: OpenApiSchemaProperty;
|
|
80
112
|
/** Schema for additional properties or boolean to allow/disallow them */
|
|
81
113
|
additionalProperties?: OpenApiSchemaProperty | boolean;
|
|
82
|
-
|
|
114
|
+
[key: string]: unknown;
|
|
115
|
+
}
|
|
83
116
|
/**
|
|
84
117
|
* Defines a parameter in an OpenAPI operation.
|
|
85
118
|
*
|
|
@@ -159,7 +192,7 @@ export interface OpenApiResponse {
|
|
|
159
192
|
*/
|
|
160
193
|
export interface OpenApiBuildResult {
|
|
161
194
|
/** The OpenAPI documentation middleware */
|
|
162
|
-
middleware:
|
|
195
|
+
middleware: express.RequestHandler;
|
|
163
196
|
/** Request body schema if defined */
|
|
164
197
|
bodySchema?: Record<string, OpenApiSchemaProperty>;
|
|
165
198
|
/** Query parameter schemas if defined */
|
|
@@ -275,7 +308,7 @@ export declare class OpenApiMiddlewareBuilder {
|
|
|
275
308
|
* });
|
|
276
309
|
* ```
|
|
277
310
|
*/
|
|
278
|
-
withRequestBody<T extends Record<string,
|
|
311
|
+
withRequestBody<T extends Record<string, unknown>>(schema: {
|
|
279
312
|
[K in keyof T]: OpenApiSchemaProperty;
|
|
280
313
|
}, options?: {
|
|
281
314
|
required?: boolean;
|
|
@@ -306,7 +339,7 @@ export declare class OpenApiMiddlewareBuilder {
|
|
|
306
339
|
* builder.withResponse(204, "No content");
|
|
307
340
|
* ```
|
|
308
341
|
*/
|
|
309
|
-
withResponse<T extends Record<string,
|
|
342
|
+
withResponse<T extends Record<string, unknown>>(statusCode: number, schema: {
|
|
310
343
|
[K in keyof T]: OpenApiSchemaProperty;
|
|
311
344
|
} | string, options?: {
|
|
312
345
|
description?: string;
|
|
@@ -334,7 +367,7 @@ export declare class OpenApiMiddlewareBuilder {
|
|
|
334
367
|
* }, {description: "List of users"});
|
|
335
368
|
* ```
|
|
336
369
|
*/
|
|
337
|
-
withArrayResponse<T extends Record<string,
|
|
370
|
+
withArrayResponse<T extends Record<string, unknown>>(statusCode: number, itemSchema: {
|
|
338
371
|
[K in keyof T]: OpenApiSchemaProperty;
|
|
339
372
|
}, options?: {
|
|
340
373
|
description?: string;
|
package/dist/openApiBuilder.js
CHANGED
|
@@ -31,37 +31,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
31
31
|
};
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
33
|
exports.createOpenApiBuilder = exports.OpenApiMiddlewareBuilder = void 0;
|
|
34
|
-
/**
|
|
35
|
-
* OpenAPI Middleware Builder
|
|
36
|
-
*
|
|
37
|
-
* This module provides a fluent builder pattern for constructing OpenAPI middleware
|
|
38
|
-
* for Express routes that don't directly map to Mongoose models. It allows you to
|
|
39
|
-
* define custom API documentation with full control over request/response schemas.
|
|
40
|
-
*
|
|
41
|
-
* @packageDocumentation
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```typescript
|
|
45
|
-
* import {createOpenApiBuilder} from "./openApiBuilder";
|
|
46
|
-
*
|
|
47
|
-
* // Create middleware with custom documentation
|
|
48
|
-
* const middleware = createOpenApiBuilder(options)
|
|
49
|
-
* .withTags(["users"])
|
|
50
|
-
* .withSummary("Get user statistics")
|
|
51
|
-
* .withDescription("Returns aggregated statistics for the current user")
|
|
52
|
-
* .withQueryParameter("period", {type: "string"}, {
|
|
53
|
-
* description: "Time period for statistics",
|
|
54
|
-
* required: false,
|
|
55
|
-
* })
|
|
56
|
-
* .withResponse<{count: number; average: number}>(200, {
|
|
57
|
-
* count: {type: "number", description: "Total count"},
|
|
58
|
-
* average: {type: "number", description: "Average value"},
|
|
59
|
-
* })
|
|
60
|
-
* .build();
|
|
61
|
-
*
|
|
62
|
-
* router.get("/stats", middleware, statsHandler);
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
34
|
var merge_1 = __importDefault(require("lodash/merge"));
|
|
66
35
|
var logger_1 = require("./logger");
|
|
67
36
|
var openApi_1 = require("./openApi");
|
|
@@ -469,6 +438,7 @@ var OpenApiMiddlewareBuilder = /** @class */ (function () {
|
|
|
469
438
|
* router.get("/users/:id", middleware, getUserHandler);
|
|
470
439
|
* ```
|
|
471
440
|
*/
|
|
441
|
+
// biome-ignore lint/suspicious/noExplicitAny: returns either a single RequestHandler or an array depending on validation config — callers spread or invoke
|
|
472
442
|
OpenApiMiddlewareBuilder.prototype.build = function () {
|
|
473
443
|
var _c, _d, _e, _f, _g;
|
|
474
444
|
var noop = function (_a, _b, next) { return next(); };
|
|
@@ -50,6 +50,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
50
50
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
51
|
};
|
|
52
52
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
53
54
|
var bun_test_1 = require("bun:test");
|
|
54
55
|
var supertest_1 = __importDefault(require("supertest"));
|
|
55
56
|
var api_1 = require("./api");
|
package/dist/openApiValidator.js
CHANGED
|
@@ -191,6 +191,7 @@ var getAjvInstance = function () {
|
|
|
191
191
|
useDefaults: true,
|
|
192
192
|
validateSchema: false,
|
|
193
193
|
});
|
|
194
|
+
// biome-ignore lint/suspicious/noExplicitAny: ajv-formats has a known type compat issue with AJV instances
|
|
194
195
|
(0, ajv_formats_1.default)(instance);
|
|
195
196
|
ajvCache.set(key, instance);
|
|
196
197
|
}
|