@terreno/api 0.11.7 → 0.11.9
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/betterAuthSetup.js +10 -3
- package/dist/configurationPlugin.d.ts +2 -1
- package/dist/configurationPlugin.js +16 -9
- package/dist/errors.d.ts +6 -6
- package/dist/errors.js +22 -22
- package/dist/errors.test.d.ts +1 -0
- package/dist/errors.test.js +280 -0
- package/dist/githubAuth.d.ts +3 -3
- package/dist/githubAuth.js +16 -16
- package/dist/middleware.d.ts +1 -1
- package/dist/middleware.js +4 -3
- package/dist/middleware.test.d.ts +1 -0
- package/dist/middleware.test.js +82 -0
- package/dist/notifiers/googleChatNotifier.js +4 -3
- package/dist/notifiers/zoomNotifier.js +12 -11
- package/dist/openApiCompat.js +2 -1
- package/dist/openApiEtag.d.ts +1 -1
- package/dist/openApiEtag.js +4 -3
- package/dist/openApiValidator.d.ts +12 -12
- package/dist/openApiValidator.js +59 -58
- package/dist/plugins.d.ts +12 -1
- package/dist/plugins.js +34 -1
- package/dist/plugins.test.js +212 -8
- package/dist/scriptRunner.d.ts +8 -7
- package/dist/secretProviders.js +17 -7
- package/dist/types/consentForm.d.ts +4 -2
- package/package.json +1 -1
- package/src/betterAuthSetup.ts +10 -3
- package/src/configurationPlugin.ts +18 -9
- package/src/errors.test.ts +302 -0
- package/src/errors.ts +18 -13
- package/src/githubAuth.ts +11 -10
- package/src/middleware.test.ts +71 -0
- package/src/middleware.ts +6 -2
- package/src/notifiers/googleChatNotifier.ts +4 -3
- package/src/notifiers/zoomNotifier.ts +4 -3
- package/src/openApiCompat.ts +2 -1
- package/src/openApiEtag.ts +2 -2
- package/src/openApiValidator.ts +46 -46
- package/src/plugins.test.ts +130 -0
- package/src/plugins.ts +35 -0
- package/src/scriptRunner.ts +23 -27
- package/src/secretProviders.ts +27 -9
- package/src/types/consentForm.ts +6 -4
package/dist/openApiValidator.js
CHANGED
|
@@ -96,18 +96,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
96
96
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
97
97
|
};
|
|
98
98
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
99
|
-
exports.isOpenApiValidatorConfigured =
|
|
100
|
-
exports.configureOpenApiValidator = configureOpenApiValidator;
|
|
101
|
-
exports.getOpenApiValidatorConfig = getOpenApiValidatorConfig;
|
|
102
|
-
exports.resetOpenApiValidatorConfig = resetOpenApiValidatorConfig;
|
|
103
|
-
exports.validateRequestBody = validateRequestBody;
|
|
104
|
-
exports.validateQueryParams = validateQueryParams;
|
|
105
|
-
exports.createValidator = createValidator;
|
|
106
|
-
exports.validateResponseData = validateResponseData;
|
|
107
|
-
exports.getSchemaFromModel = getSchemaFromModel;
|
|
108
|
-
exports.validateModelRequestBody = validateModelRequestBody;
|
|
109
|
-
exports.createModelValidators = createModelValidators;
|
|
110
|
-
exports.buildQuerySchemaFromFields = buildQuerySchemaFromFields;
|
|
99
|
+
exports.buildQuerySchemaFromFields = exports.createModelValidators = exports.validateModelRequestBody = exports.getSchemaFromModel = exports.validateResponseData = exports.createValidator = exports.validateQueryParams = exports.validateRequestBody = exports.resetOpenApiValidatorConfig = exports.getOpenApiValidatorConfig = exports.configureOpenApiValidator = exports.isOpenApiValidatorConfigured = void 0;
|
|
111
100
|
var ajv_1 = __importDefault(require("ajv"));
|
|
112
101
|
var ajv_formats_1 = __importDefault(require("ajv-formats"));
|
|
113
102
|
var mongoose_to_swagger_1 = __importDefault(require("mongoose-to-swagger"));
|
|
@@ -128,9 +117,10 @@ var globalConfig = {
|
|
|
128
117
|
* Check whether `configureOpenApiValidator()` has been called.
|
|
129
118
|
* Validation middleware is a no-op when this returns false.
|
|
130
119
|
*/
|
|
131
|
-
function
|
|
120
|
+
var isOpenApiValidatorConfigured = function () {
|
|
132
121
|
return isConfigured;
|
|
133
|
-
}
|
|
122
|
+
};
|
|
123
|
+
exports.isOpenApiValidatorConfigured = isOpenApiValidatorConfigured;
|
|
134
124
|
/**
|
|
135
125
|
* Configure the global OpenAPI validator settings.
|
|
136
126
|
* Calling this function activates validation — middleware that was previously
|
|
@@ -148,7 +138,7 @@ function isOpenApiValidatorConfigured() {
|
|
|
148
138
|
* });
|
|
149
139
|
* ```
|
|
150
140
|
*/
|
|
151
|
-
function
|
|
141
|
+
var configureOpenApiValidator = function (config) {
|
|
152
142
|
if (config === void 0) { config = {}; }
|
|
153
143
|
isConfigured = true;
|
|
154
144
|
globalConfig = __assign(__assign({}, globalConfig), config);
|
|
@@ -156,19 +146,21 @@ function configureOpenApiValidator(config) {
|
|
|
156
146
|
ajvCache.clear();
|
|
157
147
|
validatorCache.clear();
|
|
158
148
|
logger_1.logger.debug("OpenAPI validator configured: ".concat(JSON.stringify(globalConfig)));
|
|
159
|
-
}
|
|
149
|
+
};
|
|
150
|
+
exports.configureOpenApiValidator = configureOpenApiValidator;
|
|
160
151
|
/**
|
|
161
152
|
* Get the current global validator configuration.
|
|
162
153
|
*/
|
|
163
|
-
function
|
|
154
|
+
var getOpenApiValidatorConfig = function () {
|
|
164
155
|
return __assign({}, globalConfig);
|
|
165
|
-
}
|
|
156
|
+
};
|
|
157
|
+
exports.getOpenApiValidatorConfig = getOpenApiValidatorConfig;
|
|
166
158
|
/**
|
|
167
159
|
* Reset the global validator configuration to defaults.
|
|
168
160
|
* Also resets `isConfigured` to false.
|
|
169
161
|
* Useful for testing.
|
|
170
162
|
*/
|
|
171
|
-
function
|
|
163
|
+
var resetOpenApiValidatorConfig = function () {
|
|
172
164
|
isConfigured = false;
|
|
173
165
|
globalConfig = {
|
|
174
166
|
coerceTypes: true,
|
|
@@ -179,13 +171,14 @@ function resetOpenApiValidatorConfig() {
|
|
|
179
171
|
};
|
|
180
172
|
ajvCache.clear();
|
|
181
173
|
validatorCache.clear();
|
|
182
|
-
}
|
|
174
|
+
};
|
|
175
|
+
exports.resetOpenApiValidatorConfig = resetOpenApiValidatorConfig;
|
|
183
176
|
// Lazy AJV instance cache keyed by coerceTypes + removeAdditional
|
|
184
177
|
var ajvCache = new Map();
|
|
185
178
|
/**
|
|
186
179
|
* Get or create an AJV instance with the current config settings.
|
|
187
180
|
*/
|
|
188
|
-
function
|
|
181
|
+
var getAjvInstance = function () {
|
|
189
182
|
var _a, _b, _c, _d;
|
|
190
183
|
var key = "coerce:".concat((_a = globalConfig.coerceTypes) !== null && _a !== void 0 ? _a : true, ",remove:").concat((_b = globalConfig.removeAdditional) !== null && _b !== void 0 ? _b : true);
|
|
191
184
|
var instance = ajvCache.get(key);
|
|
@@ -202,15 +195,15 @@ function getAjvInstance() {
|
|
|
202
195
|
ajvCache.set(key, instance);
|
|
203
196
|
}
|
|
204
197
|
return instance;
|
|
205
|
-
}
|
|
198
|
+
};
|
|
206
199
|
// Cache compiled validators by schema hash + config key
|
|
207
200
|
var validatorCache = new Map();
|
|
208
201
|
/**
|
|
209
202
|
* Generate a simple hash for a schema to use as a cache key.
|
|
210
203
|
*/
|
|
211
|
-
function
|
|
204
|
+
var hashSchema = function (schema) {
|
|
212
205
|
return JSON.stringify(schema);
|
|
213
|
-
}
|
|
206
|
+
};
|
|
214
207
|
var VALID_JSON_SCHEMA_TYPES = new Set([
|
|
215
208
|
"string",
|
|
216
209
|
"number",
|
|
@@ -229,7 +222,7 @@ var MONGOOSE_TYPE_MAP = {
|
|
|
229
222
|
* Recursively replace non-standard mongoose-to-swagger types with valid JSON Schema types
|
|
230
223
|
* so AJV can compile the schema.
|
|
231
224
|
*/
|
|
232
|
-
function
|
|
225
|
+
var sanitizeSchemaForAjv = function (schema) {
|
|
233
226
|
var e_1, _a;
|
|
234
227
|
if (!schema || typeof schema !== "object") {
|
|
235
228
|
return schema;
|
|
@@ -274,14 +267,14 @@ function sanitizeSchemaForAjv(schema) {
|
|
|
274
267
|
result.additionalProperties = sanitizeSchemaForAjv(result.additionalProperties);
|
|
275
268
|
}
|
|
276
269
|
return result;
|
|
277
|
-
}
|
|
270
|
+
};
|
|
278
271
|
/**
|
|
279
272
|
* Get or create a compiled validator for a schema.
|
|
280
273
|
* Uses the current config so changes take effect on next call.
|
|
281
274
|
* Sanitizes non-standard mongoose-to-swagger types before compilation.
|
|
282
275
|
* Returns null if the schema still cannot be compiled after sanitization.
|
|
283
276
|
*/
|
|
284
|
-
function
|
|
277
|
+
var getValidator = function (schema) {
|
|
285
278
|
var _a, _b;
|
|
286
279
|
var ajv = getAjvInstance();
|
|
287
280
|
var configKey = "coerce:".concat((_a = globalConfig.coerceTypes) !== null && _a !== void 0 ? _a : true, ",remove:").concat((_b = globalConfig.removeAdditional) !== null && _b !== void 0 ? _b : true);
|
|
@@ -301,11 +294,11 @@ function getValidator(schema) {
|
|
|
301
294
|
validatorCache.set(hash, null);
|
|
302
295
|
return null;
|
|
303
296
|
}
|
|
304
|
-
}
|
|
297
|
+
};
|
|
305
298
|
/**
|
|
306
299
|
* Format AJV errors into a human-readable string.
|
|
307
300
|
*/
|
|
308
|
-
function
|
|
301
|
+
var formatValidationErrors = function (errors) {
|
|
309
302
|
return errors
|
|
310
303
|
.map(function (err) {
|
|
311
304
|
var path = err.instancePath || "/";
|
|
@@ -313,13 +306,13 @@ function formatValidationErrors(errors) {
|
|
|
313
306
|
return "".concat(path, ": ").concat(message);
|
|
314
307
|
})
|
|
315
308
|
.join("; ");
|
|
316
|
-
}
|
|
309
|
+
};
|
|
317
310
|
/**
|
|
318
311
|
* Convert OpenApiSchemaProperty to a full OpenApiSchema suitable for AJV.
|
|
319
312
|
* Strips `required` from individual properties (OpenAPI-style) and moves it
|
|
320
313
|
* to the schema-level `required` array (JSON Schema-style) for AJV compatibility.
|
|
321
314
|
*/
|
|
322
|
-
function
|
|
315
|
+
var propertiesToSchema = function (properties, requiredFields) {
|
|
323
316
|
var e_2, _a;
|
|
324
317
|
// Extract required fields from properties that have required: true
|
|
325
318
|
var autoRequired = Object.entries(properties)
|
|
@@ -359,7 +352,7 @@ function propertiesToSchema(properties, requiredFields) {
|
|
|
359
352
|
schema.additionalProperties = false;
|
|
360
353
|
}
|
|
361
354
|
return schema;
|
|
362
|
-
}
|
|
355
|
+
};
|
|
363
356
|
/**
|
|
364
357
|
* Creates middleware that validates the request body against an OpenAPI schema.
|
|
365
358
|
*
|
|
@@ -370,7 +363,7 @@ function propertiesToSchema(properties, requiredFields) {
|
|
|
370
363
|
* @param options - Optional configuration for this validator
|
|
371
364
|
* @returns Express middleware function
|
|
372
365
|
*/
|
|
373
|
-
function
|
|
366
|
+
var validateRequestBody = function (schema, options) {
|
|
374
367
|
var fullSchema = propertiesToSchema(schema, options === null || options === void 0 ? void 0 : options.required);
|
|
375
368
|
return function (req, _res, next) {
|
|
376
369
|
var _a, _b, _c;
|
|
@@ -447,7 +440,8 @@ function validateRequestBody(schema, options) {
|
|
|
447
440
|
}
|
|
448
441
|
next();
|
|
449
442
|
};
|
|
450
|
-
}
|
|
443
|
+
};
|
|
444
|
+
exports.validateRequestBody = validateRequestBody;
|
|
451
445
|
/**
|
|
452
446
|
* Creates middleware that validates query parameters against an OpenAPI schema.
|
|
453
447
|
*
|
|
@@ -455,7 +449,7 @@ function validateRequestBody(schema, options) {
|
|
|
455
449
|
* @param options - Optional configuration for this validator
|
|
456
450
|
* @returns Express middleware function
|
|
457
451
|
*/
|
|
458
|
-
function
|
|
452
|
+
var validateQueryParams = function (schema, options) {
|
|
459
453
|
var fullSchema = propertiesToSchema(schema);
|
|
460
454
|
return function (req, _res, next) {
|
|
461
455
|
var _a, _b, _c, _d;
|
|
@@ -513,7 +507,8 @@ function validateQueryParams(schema, options) {
|
|
|
513
507
|
}
|
|
514
508
|
next();
|
|
515
509
|
};
|
|
516
|
-
}
|
|
510
|
+
};
|
|
511
|
+
exports.validateQueryParams = validateQueryParams;
|
|
517
512
|
/**
|
|
518
513
|
* Creates a combined validation middleware for both body and query parameters.
|
|
519
514
|
*
|
|
@@ -531,12 +526,12 @@ function validateQueryParams(schema, options) {
|
|
|
531
526
|
* ], handler);
|
|
532
527
|
* ```
|
|
533
528
|
*/
|
|
534
|
-
function
|
|
529
|
+
var createValidator = function (options) {
|
|
535
530
|
var bodyValidator = options.body
|
|
536
|
-
? validateRequestBody(options.body, { enabled: options.enabled })
|
|
531
|
+
? (0, exports.validateRequestBody)(options.body, { enabled: options.enabled })
|
|
537
532
|
: null;
|
|
538
533
|
var queryValidator = options.query
|
|
539
|
-
? validateQueryParams(options.query, { enabled: options.enabled })
|
|
534
|
+
? (0, exports.validateQueryParams)(options.query, { enabled: options.enabled })
|
|
540
535
|
: null;
|
|
541
536
|
return function (req, res, next) {
|
|
542
537
|
// Run body validation first
|
|
@@ -562,7 +557,8 @@ function createValidator(options) {
|
|
|
562
557
|
next();
|
|
563
558
|
}
|
|
564
559
|
};
|
|
565
|
-
}
|
|
560
|
+
};
|
|
561
|
+
exports.createValidator = createValidator;
|
|
566
562
|
/**
|
|
567
563
|
* Validates response data against a schema.
|
|
568
564
|
* This is primarily for development/testing to ensure responses match documentation.
|
|
@@ -571,7 +567,7 @@ function createValidator(options) {
|
|
|
571
567
|
* @param schema - The expected schema
|
|
572
568
|
* @returns Object with valid flag and any errors
|
|
573
569
|
*/
|
|
574
|
-
function
|
|
570
|
+
var validateResponseData = function (data, schema) {
|
|
575
571
|
if (!globalConfig.validateResponses) {
|
|
576
572
|
return { valid: true };
|
|
577
573
|
}
|
|
@@ -588,7 +584,8 @@ function validateResponseData(data, schema) {
|
|
|
588
584
|
return { errors: validator.errors, valid: false };
|
|
589
585
|
}
|
|
590
586
|
return { valid: true };
|
|
591
|
-
}
|
|
587
|
+
};
|
|
588
|
+
exports.validateResponseData = validateResponseData;
|
|
592
589
|
var m2sOptions = {
|
|
593
590
|
props: ["readOnly", "required", "enum", "default"],
|
|
594
591
|
};
|
|
@@ -600,19 +597,20 @@ var m2sOptions = {
|
|
|
600
597
|
* @param model - A Mongoose model
|
|
601
598
|
* @returns Schema properties suitable for validation
|
|
602
599
|
*/
|
|
603
|
-
function
|
|
600
|
+
var getSchemaFromModel = function (model) {
|
|
604
601
|
var modelSwagger = (0, mongoose_to_swagger_1.default)(model, m2sOptions);
|
|
605
602
|
(0, populate_1.fixMixedFields)(model.schema, modelSwagger.properties);
|
|
606
603
|
return modelSwagger.properties;
|
|
607
|
-
}
|
|
604
|
+
};
|
|
605
|
+
exports.getSchemaFromModel = getSchemaFromModel;
|
|
608
606
|
/**
|
|
609
607
|
* Extract required field names from a Mongoose model's swagger schema.
|
|
610
608
|
*/
|
|
611
|
-
function
|
|
609
|
+
var getRequiredFieldsFromModel = function (model) {
|
|
612
610
|
var _a;
|
|
613
611
|
var modelSwagger = (0, mongoose_to_swagger_1.default)(model, m2sOptions);
|
|
614
612
|
return (_a = modelSwagger.required) !== null && _a !== void 0 ? _a : [];
|
|
615
|
-
}
|
|
613
|
+
};
|
|
616
614
|
/**
|
|
617
615
|
* Creates a request body validator middleware from a Mongoose model.
|
|
618
616
|
* This is a convenience function that combines getSchemaFromModel and validateRequestBody.
|
|
@@ -621,9 +619,9 @@ function getRequiredFieldsFromModel(model) {
|
|
|
621
619
|
* @param options - Optional configuration for the validator
|
|
622
620
|
* @returns Express middleware function
|
|
623
621
|
*/
|
|
624
|
-
function
|
|
622
|
+
var validateModelRequestBody = function (model, options) {
|
|
625
623
|
var _a, _b;
|
|
626
|
-
var schema = getSchemaFromModel(model);
|
|
624
|
+
var schema = (0, exports.getSchemaFromModel)(model);
|
|
627
625
|
var requiredFields = getRequiredFieldsFromModel(model);
|
|
628
626
|
if ((_a = options === null || options === void 0 ? void 0 : options.excludeFields) === null || _a === void 0 ? void 0 : _a.length) {
|
|
629
627
|
var excluded_1 = new Set(options.excludeFields);
|
|
@@ -633,8 +631,9 @@ function validateModelRequestBody(model, options) {
|
|
|
633
631
|
}));
|
|
634
632
|
requiredFields = requiredFields.filter(function (f) { return !excluded_1.has(f); });
|
|
635
633
|
}
|
|
636
|
-
return validateRequestBody(schema, __assign(__assign({}, options), { required: __spreadArray(__spreadArray([], __read(((_b = options === null || options === void 0 ? void 0 : options.required) !== null && _b !== void 0 ? _b : [])), false), __read(requiredFields), false) }));
|
|
637
|
-
}
|
|
634
|
+
return (0, exports.validateRequestBody)(schema, __assign(__assign({}, options), { required: __spreadArray(__spreadArray([], __read(((_b = options === null || options === void 0 ? void 0 : options.required) !== null && _b !== void 0 ? _b : [])), false), __read(requiredFields), false) }));
|
|
635
|
+
};
|
|
636
|
+
exports.validateModelRequestBody = validateModelRequestBody;
|
|
638
637
|
/**
|
|
639
638
|
* Creates validation middleware for use with modelRouter.
|
|
640
639
|
* Returns an object with middleware for each operation type.
|
|
@@ -643,21 +642,22 @@ function validateModelRequestBody(model, options) {
|
|
|
643
642
|
* @param options - Configuration options
|
|
644
643
|
* @returns Object with create and update validation middleware
|
|
645
644
|
*/
|
|
646
|
-
function
|
|
647
|
-
var schema = getSchemaFromModel(model);
|
|
645
|
+
var createModelValidators = function (model, options) {
|
|
646
|
+
var schema = (0, exports.getSchemaFromModel)(model);
|
|
648
647
|
return {
|
|
649
|
-
create: validateRequestBody(schema, {
|
|
648
|
+
create: (0, exports.validateRequestBody)(schema, {
|
|
650
649
|
enabled: options === null || options === void 0 ? void 0 : options.validateCreate,
|
|
651
650
|
onAdditionalPropertiesRemoved: options === null || options === void 0 ? void 0 : options.onAdditionalPropertiesRemoved,
|
|
652
651
|
onError: options === null || options === void 0 ? void 0 : options.onError,
|
|
653
652
|
}),
|
|
654
|
-
update: validateRequestBody(schema, {
|
|
653
|
+
update: (0, exports.validateRequestBody)(schema, {
|
|
655
654
|
enabled: options === null || options === void 0 ? void 0 : options.validateUpdate,
|
|
656
655
|
onAdditionalPropertiesRemoved: options === null || options === void 0 ? void 0 : options.onAdditionalPropertiesRemoved,
|
|
657
656
|
onError: options === null || options === void 0 ? void 0 : options.onError,
|
|
658
657
|
}),
|
|
659
658
|
};
|
|
660
|
-
}
|
|
659
|
+
};
|
|
660
|
+
exports.createModelValidators = createModelValidators;
|
|
661
661
|
/**
|
|
662
662
|
* Build a query parameter schema from a model's Mongoose schema and queryFields array.
|
|
663
663
|
* Always includes pagination parameters (limit, page, sort).
|
|
@@ -666,10 +666,10 @@ function createModelValidators(model, options) {
|
|
|
666
666
|
* @param queryFields - Array of field names allowed for querying
|
|
667
667
|
* @returns Schema properties suitable for query validation
|
|
668
668
|
*/
|
|
669
|
-
function
|
|
669
|
+
var buildQuerySchemaFromFields = function (model, queryFields) {
|
|
670
670
|
var e_3, _a;
|
|
671
671
|
if (queryFields === void 0) { queryFields = []; }
|
|
672
|
-
var modelSchema = getSchemaFromModel(model);
|
|
672
|
+
var modelSchema = (0, exports.getSchemaFromModel)(model);
|
|
673
673
|
var querySchema = {
|
|
674
674
|
limit: { type: "number" },
|
|
675
675
|
page: { type: "number" },
|
|
@@ -697,4 +697,5 @@ function buildQuerySchemaFromFields(model, queryFields) {
|
|
|
697
697
|
finally { if (e_3) throw e_3.error; }
|
|
698
698
|
}
|
|
699
699
|
return querySchema;
|
|
700
|
-
}
|
|
700
|
+
};
|
|
701
|
+
exports.buildQuerySchemaFromFields = buildQuerySchemaFromFields;
|
package/dist/plugins.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Document, type Schema, SchemaType, type SchemaTypeOptions } from "mongoose";
|
|
1
|
+
import mongoose, { type Document, type FilterQuery, type Schema, SchemaType, type SchemaTypeOptions } from "mongoose";
|
|
2
2
|
import { type APIErrorConstructor } from "./errors";
|
|
3
3
|
export interface BaseUser {
|
|
4
4
|
admin: boolean;
|
|
@@ -31,6 +31,17 @@ export declare const firebaseJWTPlugin: (schema: Schema) => void;
|
|
|
31
31
|
* @param schema Mongoose Schema
|
|
32
32
|
*/
|
|
33
33
|
export declare const findOneOrNone: <T>(schema: Schema<T>) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Helper that performs a `findOneOrNone` lookup against any Mongoose model. Returns the matching
|
|
36
|
+
* document, `null` if none match, or throws if more than one matches. If the model's schema has
|
|
37
|
+
* the {@link findOneOrNone} plugin applied, the plugin static is used; otherwise the lookup is
|
|
38
|
+
* performed directly via `model.find(...)`. Prefer this helper from framework code where the
|
|
39
|
+
* consumer's model may or may not have the plugin installed.
|
|
40
|
+
* @param model Mongoose Model
|
|
41
|
+
* @param query Mongoose query object
|
|
42
|
+
* @param errorArgs Optional overrides for the thrown {@link APIError} when multiple match
|
|
43
|
+
*/
|
|
44
|
+
export declare const findOneOrNoneFor: <T>(model: mongoose.Model<T>, query: FilterQuery<T>, errorArgs?: Partial<APIErrorConstructor>) => Promise<(Document & T) | null>;
|
|
34
45
|
/**
|
|
35
46
|
* This adds a static method `Model.findExactlyOne` to the schema. This or findOneOrNone should replace `Model.findOne`
|
|
36
47
|
* in most instances.
|
package/dist/plugins.js
CHANGED
|
@@ -95,7 +95,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
95
95
|
}
|
|
96
96
|
};
|
|
97
97
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
98
|
-
exports.DateOnly = exports.upsertPlugin = exports.findExactlyOne = exports.findOneOrNone = exports.firebaseJWTPlugin = exports.createdUpdatedPlugin = exports.isDisabledPlugin = exports.isDeletedPlugin = exports.baseUserPlugin = void 0;
|
|
98
|
+
exports.DateOnly = exports.upsertPlugin = exports.findExactlyOne = exports.findOneOrNoneFor = exports.findOneOrNone = exports.firebaseJWTPlugin = exports.createdUpdatedPlugin = exports.isDisabledPlugin = exports.isDeletedPlugin = exports.baseUserPlugin = void 0;
|
|
99
99
|
var luxon_1 = require("luxon");
|
|
100
100
|
var mongoose_1 = __importStar(require("mongoose"));
|
|
101
101
|
var errors_1 = require("./errors");
|
|
@@ -199,6 +199,39 @@ var findOneOrNone = function (schema) {
|
|
|
199
199
|
};
|
|
200
200
|
};
|
|
201
201
|
exports.findOneOrNone = findOneOrNone;
|
|
202
|
+
/**
|
|
203
|
+
* Helper that performs a `findOneOrNone` lookup against any Mongoose model. Returns the matching
|
|
204
|
+
* document, `null` if none match, or throws if more than one matches. If the model's schema has
|
|
205
|
+
* the {@link findOneOrNone} plugin applied, the plugin static is used; otherwise the lookup is
|
|
206
|
+
* performed directly via `model.find(...)`. Prefer this helper from framework code where the
|
|
207
|
+
* consumer's model may or may not have the plugin installed.
|
|
208
|
+
* @param model Mongoose Model
|
|
209
|
+
* @param query Mongoose query object
|
|
210
|
+
* @param errorArgs Optional overrides for the thrown {@link APIError} when multiple match
|
|
211
|
+
*/
|
|
212
|
+
var findOneOrNoneFor = function (model, query, errorArgs) { return __awaiter(void 0, void 0, void 0, function () {
|
|
213
|
+
var withStatic, results;
|
|
214
|
+
return __generator(this, function (_a) {
|
|
215
|
+
switch (_a.label) {
|
|
216
|
+
case 0:
|
|
217
|
+
withStatic = model;
|
|
218
|
+
if (typeof withStatic.findOneOrNone === "function") {
|
|
219
|
+
return [2 /*return*/, withStatic.findOneOrNone(query, errorArgs)];
|
|
220
|
+
}
|
|
221
|
+
return [4 /*yield*/, model.find(query)];
|
|
222
|
+
case 1:
|
|
223
|
+
results = _a.sent();
|
|
224
|
+
if (results.length === 0) {
|
|
225
|
+
return [2 /*return*/, null];
|
|
226
|
+
}
|
|
227
|
+
if (results.length > 1) {
|
|
228
|
+
throw new errors_1.APIError(__assign({ detail: "query: ".concat(JSON.stringify(query)), status: 500, title: "".concat(model.modelName, ".findOne query returned multiple documents") }, errorArgs));
|
|
229
|
+
}
|
|
230
|
+
return [2 /*return*/, results[0]];
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}); };
|
|
234
|
+
exports.findOneOrNoneFor = findOneOrNoneFor;
|
|
202
235
|
/**
|
|
203
236
|
* This adds a static method `Model.findExactlyOne` to the schema. This or findOneOrNone should replace `Model.findOne`
|
|
204
237
|
* in most instances.
|
package/dist/plugins.test.js
CHANGED
|
@@ -74,6 +74,28 @@ stuffSchema.plugin(plugins_1.findExactlyOne);
|
|
|
74
74
|
stuffSchema.plugin(plugins_1.upsertPlugin);
|
|
75
75
|
stuffSchema.plugin(plugins_1.createdUpdatedPlugin);
|
|
76
76
|
var StuffModel = (0, mongoose_1.model)("Stuff", stuffSchema);
|
|
77
|
+
(0, bun_test_1.describe)("baseUserPlugin", function () {
|
|
78
|
+
(0, bun_test_1.it)("adds admin and email fields to the schema", function () {
|
|
79
|
+
var testSchema = new mongoose_1.Schema({});
|
|
80
|
+
// biome-ignore lint/suspicious/noExplicitAny: test schema
|
|
81
|
+
(0, plugins_1.baseUserPlugin)(testSchema);
|
|
82
|
+
var adminPath = testSchema.path("admin");
|
|
83
|
+
(0, bun_test_1.expect)(adminPath).toBeDefined();
|
|
84
|
+
(0, bun_test_1.expect)(adminPath.options.default).toBe(false);
|
|
85
|
+
var emailPath = testSchema.path("email");
|
|
86
|
+
(0, bun_test_1.expect)(emailPath).toBeDefined();
|
|
87
|
+
(0, bun_test_1.expect)(emailPath.options.index).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
(0, bun_test_1.describe)("firebaseJWTPlugin", function () {
|
|
91
|
+
(0, bun_test_1.it)("adds firebaseId field to the schema", function () {
|
|
92
|
+
var testSchema = new mongoose_1.Schema({});
|
|
93
|
+
(0, plugins_1.firebaseJWTPlugin)(testSchema);
|
|
94
|
+
var firebaseIdPath = testSchema.path("firebaseId");
|
|
95
|
+
(0, bun_test_1.expect)(firebaseIdPath).toBeDefined();
|
|
96
|
+
(0, bun_test_1.expect)(firebaseIdPath.options.index).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
77
99
|
(0, bun_test_1.describe)("createdUpdate", function () {
|
|
78
100
|
(0, bun_test_1.it)("sets created and updated on save", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
79
101
|
var stuff;
|
|
@@ -256,6 +278,188 @@ var StuffModel = (0, mongoose_1.model)("Stuff", stuffSchema);
|
|
|
256
278
|
});
|
|
257
279
|
}); });
|
|
258
280
|
});
|
|
281
|
+
var bareThingSchema = new mongoose_1.Schema({
|
|
282
|
+
name: { description: "The name of the bare item", type: String },
|
|
283
|
+
ownerId: { description: "The owner of the bare item", type: String },
|
|
284
|
+
});
|
|
285
|
+
var BareThingModel = (0, mongoose_1.model)("BareThing", bareThingSchema);
|
|
286
|
+
(0, bun_test_1.describe)("findOneOrNoneFor", function () {
|
|
287
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
288
|
+
return __generator(this, function (_a) {
|
|
289
|
+
switch (_a.label) {
|
|
290
|
+
case 0: return [4 /*yield*/, StuffModel.deleteMany({})];
|
|
291
|
+
case 1:
|
|
292
|
+
_a.sent();
|
|
293
|
+
return [4 /*yield*/, BareThingModel.deleteMany({})];
|
|
294
|
+
case 2:
|
|
295
|
+
_a.sent();
|
|
296
|
+
return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
297
|
+
case 3:
|
|
298
|
+
_a.sent();
|
|
299
|
+
return [2 /*return*/];
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}); });
|
|
303
|
+
(0, bun_test_1.describe)("when the schema has the findOneOrNone plugin", function () {
|
|
304
|
+
var things;
|
|
305
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
306
|
+
var _a;
|
|
307
|
+
return __generator(this, function (_b) {
|
|
308
|
+
switch (_b.label) {
|
|
309
|
+
case 0: return [4 /*yield*/, Promise.all([
|
|
310
|
+
StuffModel.create({ name: "Things", ownerId: "123" }),
|
|
311
|
+
StuffModel.create({ name: "StuffNThings", ownerId: "123" }),
|
|
312
|
+
])];
|
|
313
|
+
case 1:
|
|
314
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), things = _a[0];
|
|
315
|
+
return [2 /*return*/];
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}); });
|
|
319
|
+
(0, bun_test_1.it)("returns null with no matches", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
320
|
+
var result;
|
|
321
|
+
return __generator(this, function (_a) {
|
|
322
|
+
switch (_a.label) {
|
|
323
|
+
case 0: return [4 /*yield*/, (0, plugins_1.findOneOrNoneFor)(StuffModel, { name: "OtherStuff" })];
|
|
324
|
+
case 1:
|
|
325
|
+
result = _a.sent();
|
|
326
|
+
(0, bun_test_1.expect)(result).toBeNull();
|
|
327
|
+
return [2 /*return*/];
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
}); });
|
|
331
|
+
(0, bun_test_1.it)("returns a single match", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
332
|
+
var result;
|
|
333
|
+
return __generator(this, function (_a) {
|
|
334
|
+
switch (_a.label) {
|
|
335
|
+
case 0: return [4 /*yield*/, (0, plugins_1.findOneOrNoneFor)(StuffModel, { name: "Things" })];
|
|
336
|
+
case 1:
|
|
337
|
+
result = _a.sent();
|
|
338
|
+
(0, bun_test_1.expect)(result).not.toBeNull();
|
|
339
|
+
(0, bun_test_1.expect)(result === null || result === void 0 ? void 0 : result._id.toString()).toBe(things._id.toString());
|
|
340
|
+
return [2 /*return*/];
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
}); });
|
|
344
|
+
(0, bun_test_1.it)("throws when multiple documents match", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
345
|
+
var fn;
|
|
346
|
+
return __generator(this, function (_a) {
|
|
347
|
+
switch (_a.label) {
|
|
348
|
+
case 0:
|
|
349
|
+
fn = function () { return (0, plugins_1.findOneOrNoneFor)(StuffModel, { ownerId: "123" }); };
|
|
350
|
+
return [4 /*yield*/, (0, bun_test_1.expect)(fn()).rejects.toThrow(/Stuff\.findOne query returned multiple documents/)];
|
|
351
|
+
case 1:
|
|
352
|
+
_a.sent();
|
|
353
|
+
return [2 /*return*/];
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
}); });
|
|
357
|
+
(0, bun_test_1.it)("forwards errorArgs to the thrown APIError", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
358
|
+
var fn, error_2;
|
|
359
|
+
return __generator(this, function (_a) {
|
|
360
|
+
switch (_a.label) {
|
|
361
|
+
case 0:
|
|
362
|
+
fn = function () {
|
|
363
|
+
return (0, plugins_1.findOneOrNoneFor)(StuffModel, { ownerId: "123" }, { status: 400, title: "Oh no!" });
|
|
364
|
+
};
|
|
365
|
+
_a.label = 1;
|
|
366
|
+
case 1:
|
|
367
|
+
_a.trys.push([1, 3, , 4]);
|
|
368
|
+
return [4 /*yield*/, fn()];
|
|
369
|
+
case 2:
|
|
370
|
+
_a.sent();
|
|
371
|
+
throw new Error("Expected promise to reject");
|
|
372
|
+
case 3:
|
|
373
|
+
error_2 = _a.sent();
|
|
374
|
+
(0, bun_test_1.expect)(error_2.title).toBe("Oh no!");
|
|
375
|
+
(0, bun_test_1.expect)(error_2.status).toBe(400);
|
|
376
|
+
return [3 /*break*/, 4];
|
|
377
|
+
case 4: return [2 /*return*/];
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}); });
|
|
381
|
+
});
|
|
382
|
+
(0, bun_test_1.describe)("when the schema does NOT have the findOneOrNone plugin", function () {
|
|
383
|
+
var bareThings;
|
|
384
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
385
|
+
var _a;
|
|
386
|
+
return __generator(this, function (_b) {
|
|
387
|
+
switch (_b.label) {
|
|
388
|
+
case 0: return [4 /*yield*/, Promise.all([
|
|
389
|
+
BareThingModel.create({ name: "Things", ownerId: "123" }),
|
|
390
|
+
BareThingModel.create({ name: "StuffNThings", ownerId: "123" }),
|
|
391
|
+
])];
|
|
392
|
+
case 1:
|
|
393
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), bareThings = _a[0];
|
|
394
|
+
return [2 /*return*/];
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
}); });
|
|
398
|
+
(0, bun_test_1.it)("returns null with no matches", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
399
|
+
var result;
|
|
400
|
+
return __generator(this, function (_a) {
|
|
401
|
+
switch (_a.label) {
|
|
402
|
+
case 0: return [4 /*yield*/, (0, plugins_1.findOneOrNoneFor)(BareThingModel, { name: "OtherStuff" })];
|
|
403
|
+
case 1:
|
|
404
|
+
result = _a.sent();
|
|
405
|
+
(0, bun_test_1.expect)(result).toBeNull();
|
|
406
|
+
return [2 /*return*/];
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}); });
|
|
410
|
+
(0, bun_test_1.it)("returns a single match", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
411
|
+
var result;
|
|
412
|
+
var _a, _b;
|
|
413
|
+
return __generator(this, function (_c) {
|
|
414
|
+
switch (_c.label) {
|
|
415
|
+
case 0: return [4 /*yield*/, (0, plugins_1.findOneOrNoneFor)(BareThingModel, { name: "Things" })];
|
|
416
|
+
case 1:
|
|
417
|
+
result = _c.sent();
|
|
418
|
+
(0, bun_test_1.expect)(result).not.toBeNull();
|
|
419
|
+
(0, bun_test_1.expect)((_a = result === null || result === void 0 ? void 0 : result._id) === null || _a === void 0 ? void 0 : _a.toString()).toBe((_b = bareThings._id) === null || _b === void 0 ? void 0 : _b.toString());
|
|
420
|
+
return [2 /*return*/];
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}); });
|
|
424
|
+
(0, bun_test_1.it)("throws when multiple documents match", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
425
|
+
var fn;
|
|
426
|
+
return __generator(this, function (_a) {
|
|
427
|
+
switch (_a.label) {
|
|
428
|
+
case 0:
|
|
429
|
+
fn = function () { return (0, plugins_1.findOneOrNoneFor)(BareThingModel, { ownerId: "123" }); };
|
|
430
|
+
return [4 /*yield*/, (0, bun_test_1.expect)(fn()).rejects.toThrow(/BareThing\.findOne query returned multiple documents/)];
|
|
431
|
+
case 1:
|
|
432
|
+
_a.sent();
|
|
433
|
+
return [2 /*return*/];
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
}); });
|
|
437
|
+
(0, bun_test_1.it)("forwards errorArgs to the thrown APIError", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
438
|
+
var fn, error_3;
|
|
439
|
+
return __generator(this, function (_a) {
|
|
440
|
+
switch (_a.label) {
|
|
441
|
+
case 0:
|
|
442
|
+
fn = function () {
|
|
443
|
+
return (0, plugins_1.findOneOrNoneFor)(BareThingModel, { ownerId: "123" }, { status: 400, title: "Oh no!" });
|
|
444
|
+
};
|
|
445
|
+
_a.label = 1;
|
|
446
|
+
case 1:
|
|
447
|
+
_a.trys.push([1, 3, , 4]);
|
|
448
|
+
return [4 /*yield*/, fn()];
|
|
449
|
+
case 2:
|
|
450
|
+
_a.sent();
|
|
451
|
+
throw new Error("Expected promise to reject");
|
|
452
|
+
case 3:
|
|
453
|
+
error_3 = _a.sent();
|
|
454
|
+
(0, bun_test_1.expect)(error_3.title).toBe("Oh no!");
|
|
455
|
+
(0, bun_test_1.expect)(error_3.status).toBe(400);
|
|
456
|
+
return [3 /*break*/, 4];
|
|
457
|
+
case 4: return [2 /*return*/];
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
}); });
|
|
461
|
+
});
|
|
462
|
+
});
|
|
259
463
|
(0, bun_test_1.describe)("findExactlyOne", function () {
|
|
260
464
|
var things;
|
|
261
465
|
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
@@ -323,7 +527,7 @@ var StuffModel = (0, mongoose_1.model)("Stuff", stuffSchema);
|
|
|
323
527
|
});
|
|
324
528
|
}); });
|
|
325
529
|
(0, bun_test_1.it)("throws custom error with two matches.", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
326
|
-
var fn,
|
|
530
|
+
var fn, error_4;
|
|
327
531
|
return __generator(this, function (_a) {
|
|
328
532
|
switch (_a.label) {
|
|
329
533
|
case 0:
|
|
@@ -337,11 +541,11 @@ var StuffModel = (0, mongoose_1.model)("Stuff", stuffSchema);
|
|
|
337
541
|
// If the promise doesn't reject, the test should fail
|
|
338
542
|
throw new Error("Expected promise to reject");
|
|
339
543
|
case 3:
|
|
340
|
-
|
|
544
|
+
error_4 = _a.sent();
|
|
341
545
|
// Check if the error has title and status properties
|
|
342
|
-
(0, bun_test_1.expect)(
|
|
343
|
-
(0, bun_test_1.expect)(
|
|
344
|
-
(0, bun_test_1.expect)(
|
|
546
|
+
(0, bun_test_1.expect)(error_4.title).toBe("Oh no!");
|
|
547
|
+
(0, bun_test_1.expect)(error_4.status).toBe(400);
|
|
548
|
+
(0, bun_test_1.expect)(error_4.detail).toBe('query: {"ownerId":"123"}');
|
|
345
549
|
return [3 /*break*/, 4];
|
|
346
550
|
case 4: return [2 /*return*/];
|
|
347
551
|
}
|
|
@@ -504,7 +708,7 @@ var StuffModel = (0, mongoose_1.model)("Stuff", stuffSchema);
|
|
|
504
708
|
});
|
|
505
709
|
(0, bun_test_1.describe)("DateOnly", function () {
|
|
506
710
|
(0, bun_test_1.it)("throws error with invalid date", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
507
|
-
var
|
|
711
|
+
var error_5;
|
|
508
712
|
return __generator(this, function (_a) {
|
|
509
713
|
switch (_a.label) {
|
|
510
714
|
case 0:
|
|
@@ -518,8 +722,8 @@ var StuffModel = (0, mongoose_1.model)("Stuff", stuffSchema);
|
|
|
518
722
|
_a.sent();
|
|
519
723
|
return [3 /*break*/, 3];
|
|
520
724
|
case 2:
|
|
521
|
-
|
|
522
|
-
(0, bun_test_1.expect)(
|
|
725
|
+
error_5 = _a.sent();
|
|
726
|
+
(0, bun_test_1.expect)(error_5.message).toMatch(/Cast to DateOnly failed/);
|
|
523
727
|
return [2 /*return*/];
|
|
524
728
|
case 3: throw new Error("Expected error was not thrown");
|
|
525
729
|
}
|