@terreno/api 0.0.11-beta.1 → 0.0.11

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/bunfig.toml CHANGED
@@ -1,7 +1,6 @@
1
1
  [test]
2
2
  preload = ["./src/tests/bunSetup.ts"]
3
3
  root = "./src"
4
- coverage = true
5
- coverageExclude = ["dist/**", "**/*.test.ts", "**/tests/**"]
6
- coverageThreshold = { line = 80, function = 80 }
4
+ coverageExclude = ["dist/**"]
5
+ # Note: No coverageThreshold set - tests will not fail on low coverage
7
6
 
package/dist/api.d.ts CHANGED
@@ -181,6 +181,16 @@ export interface ModelRouterOptions<T> {
181
181
  * keys. Throw an APIError to return a 400 with an error message.
182
182
  */
183
183
  responseHandler?: (value: (Document<any, any, any> & T) | (Document<any, any, any> & T)[], method: "list" | "create" | "read" | "update" | "delete", request: express.Request, options: ModelRouterOptions<T>) => Promise<JSONValue>;
184
+ /**
185
+ * The discriminatorKey that you passed when creating the Mongoose models. Defaults to __t. See:
186
+ * https://mongoosejs.com/docs/discriminators.html If this key is provided,
187
+ * you must provide the same key as part of the top level of the body when making performing
188
+ * update or delete operations on this model.
189
+ * \{discriminatorKey: "__t"\}
190
+ *
191
+ * PATCH \{__t: "SuperUser", name: "Foo"\} // __t is required or there will be a 404 error.
192
+ */
193
+ discriminatorKey?: string;
184
194
  /**
185
195
  * The OpenAPI generator for this server. This is used to generate the OpenAPI documentation.
186
196
  */
@@ -204,13 +214,14 @@ export interface ModelRouterOptions<T> {
204
214
  */
205
215
  openApiExtraModelProperties?: any;
206
216
  }
217
+ export declare function getModel(baseModel: Model<any>, body?: any, options?: ModelRouterOptions<any>): mongoose.Model<any, {}, {}, {}, any, any, any>;
207
218
  /**
208
- * Create a set of CRUD routes given a Mongoose model and configuration options.
219
+ * Create a set of CRUD routes given a Mongoose model $baseModel and configuration options.
209
220
  *
210
- * @param model A Mongoose Model
221
+ * @param baseModel A Mongoose Model
211
222
  * @param options Options for configuring the REST API, such as permissions, transformers, and hooks.
212
223
  */
213
- export declare function modelRouter<T>(model: Model<T>, options: ModelRouterOptions<T>): express.Router;
224
+ export declare function modelRouter<T>(baseModel: Model<T>, options: ModelRouterOptions<T>): express.Router;
214
225
  export declare const asyncHandler: (fn: any) => (req: Request, res: Response, next: NextFunction) => Promise<any>;
215
226
  export declare const gooseRestRouter: typeof modelRouter;
216
227
  export type GooseRESTOptions<T> = ModelRouterOptions<T>;
package/dist/api.js CHANGED
@@ -121,6 +121,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
121
121
  Object.defineProperty(exports, "__esModule", { value: true });
122
122
  exports.gooseRestRouter = exports.asyncHandler = void 0;
123
123
  exports.addPopulateToQuery = addPopulateToQuery;
124
+ exports.getModel = getModel;
124
125
  exports.modelRouter = modelRouter;
125
126
  /**
126
127
  * This is the doc comment for api.ts
@@ -167,6 +168,21 @@ function addPopulateToQuery(builtQuery, populatePaths) {
167
168
  var PAGINATION_QUERY_PARAMS = ["limit", "page", "sort"];
168
169
  // Add support for more complex queries.
169
170
  var COMPLEX_QUERY_PARAMS = ["$and", "$or"];
171
+ // A function to decide which model to use. If no discriminators are provided,
172
+ // just returns the base model. If
173
+ function getModel(baseModel, body, options) {
174
+ var _a, _b;
175
+ var discriminatorKey = (_a = options === null || options === void 0 ? void 0 : options.discriminatorKey) !== null && _a !== void 0 ? _a : "__t";
176
+ var modelName = body === null || body === void 0 ? void 0 : body[discriminatorKey];
177
+ if (!modelName) {
178
+ return baseModel;
179
+ }
180
+ var model = (_b = baseModel.discriminators) === null || _b === void 0 ? void 0 : _b[modelName];
181
+ if (!model) {
182
+ throw new Error("Could not find discriminator model for key ".concat(modelName, ", baseModel: ").concat(baseModel));
183
+ }
184
+ return model;
185
+ }
170
186
  // Ensures query params are allowed. Also checks nested query params when using $and/$or.
171
187
  function checkQueryParamAllowed(queryParam, queryParamValue, queryFields) {
172
188
  var e_2, _a, e_3, _b;
@@ -228,12 +244,12 @@ function checkQueryParamAllowed(queryParam, queryParamValue, queryFields) {
228
244
  // return result;
229
245
  // }
230
246
  /**
231
- * Create a set of CRUD routes given a Mongoose model and configuration options.
247
+ * Create a set of CRUD routes given a Mongoose model $baseModel and configuration options.
232
248
  *
233
- * @param model A Mongoose Model
249
+ * @param baseModel A Mongoose Model
234
250
  * @param options Options for configuring the REST API, such as permissions, transformers, and hooks.
235
251
  */
236
- function modelRouter(model, options) {
252
+ function modelRouter(baseModel, options) {
237
253
  var _this = this;
238
254
  var _a;
239
255
  var router = express_1.default.Router();
@@ -244,13 +260,15 @@ function modelRouter(model, options) {
244
260
  var responseHandler = (_a = options.responseHandler) !== null && _a !== void 0 ? _a : transformers_1.defaultResponseHandler;
245
261
  router.post("/", [
246
262
  (0, auth_1.authenticateMiddleware)(options.allowAnonymous),
247
- (0, openApi_1.createOpenApiMiddleware)(model, options),
248
- (0, permissions_1.permissionMiddleware)(model, options),
263
+ (0, openApi_1.createOpenApiMiddleware)(baseModel, options),
264
+ (0, permissions_1.permissionMiddleware)(baseModel, options),
249
265
  ], (0, exports.asyncHandler)(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
250
- var body, error_1, data, error_2, populateQuery, error_3, error_4, serialized, error_5;
251
- return __generator(this, function (_a) {
252
- switch (_a.label) {
266
+ var model, body, error_1, data, error_2, populateQuery, error_3, error_4, serialized, error_5;
267
+ var _a;
268
+ return __generator(this, function (_b) {
269
+ switch (_b.label) {
253
270
  case 0:
271
+ model = getModel(baseModel, (_a = req.body) === null || _a === void 0 ? void 0 : _a.__t, options);
254
272
  try {
255
273
  body = (0, transformers_1.transform)(options, req.body, "create", req.user);
256
274
  }
@@ -263,15 +281,15 @@ function modelRouter(model, options) {
263
281
  });
264
282
  }
265
283
  if (!options.preCreate) return [3 /*break*/, 5];
266
- _a.label = 1;
284
+ _b.label = 1;
267
285
  case 1:
268
- _a.trys.push([1, 3, , 4]);
286
+ _b.trys.push([1, 3, , 4]);
269
287
  return [4 /*yield*/, options.preCreate(body, req)];
270
288
  case 2:
271
- body = _a.sent();
289
+ body = _b.sent();
272
290
  return [3 /*break*/, 4];
273
291
  case 3:
274
- error_1 = _a.sent();
292
+ error_1 = _b.sent();
275
293
  if ((0, errors_1.isAPIError)(error_1)) {
276
294
  throw error_1;
277
295
  }
@@ -296,7 +314,7 @@ function modelRouter(model, options) {
296
314
  title: "Create not allowed",
297
315
  });
298
316
  }
299
- _a.label = 5;
317
+ _b.label = 5;
300
318
  case 5:
301
319
  if (body === undefined) {
302
320
  throw new errors_1.APIError({
@@ -305,15 +323,15 @@ function modelRouter(model, options) {
305
323
  title: "Invalid request body",
306
324
  });
307
325
  }
308
- _a.label = 6;
326
+ _b.label = 6;
309
327
  case 6:
310
- _a.trys.push([6, 8, , 9]);
328
+ _b.trys.push([6, 8, , 9]);
311
329
  return [4 /*yield*/, model.create(body)];
312
330
  case 7:
313
- data = _a.sent();
331
+ data = _b.sent();
314
332
  return [3 /*break*/, 9];
315
333
  case 8:
316
- error_2 = _a.sent();
334
+ error_2 = _b.sent();
317
335
  throw new errors_1.APIError({
318
336
  disableExternalErrorTracking: (0, errors_1.getDisableExternalErrorTracking)(error_2),
319
337
  error: error_2,
@@ -322,17 +340,17 @@ function modelRouter(model, options) {
322
340
  });
323
341
  case 9:
324
342
  if (!options.populatePaths) return [3 /*break*/, 13];
325
- _a.label = 10;
343
+ _b.label = 10;
326
344
  case 10:
327
- _a.trys.push([10, 12, , 13]);
345
+ _b.trys.push([10, 12, , 13]);
328
346
  populateQuery = model.findById(data._id);
329
347
  populateQuery = addPopulateToQuery(populateQuery, options.populatePaths);
330
348
  return [4 /*yield*/, populateQuery.exec()];
331
349
  case 11:
332
- data = _a.sent();
350
+ data = _b.sent();
333
351
  return [3 /*break*/, 13];
334
352
  case 12:
335
- error_3 = _a.sent();
353
+ error_3 = _b.sent();
336
354
  throw new errors_1.APIError({
337
355
  disableExternalErrorTracking: (0, errors_1.getDisableExternalErrorTracking)(error_3),
338
356
  error: error_3,
@@ -341,15 +359,15 @@ function modelRouter(model, options) {
341
359
  });
342
360
  case 13:
343
361
  if (!options.postCreate) return [3 /*break*/, 17];
344
- _a.label = 14;
362
+ _b.label = 14;
345
363
  case 14:
346
- _a.trys.push([14, 16, , 17]);
364
+ _b.trys.push([14, 16, , 17]);
347
365
  return [4 /*yield*/, options.postCreate(data, req)];
348
366
  case 15:
349
- _a.sent();
367
+ _b.sent();
350
368
  return [3 /*break*/, 17];
351
369
  case 16:
352
- error_4 = _a.sent();
370
+ error_4 = _b.sent();
353
371
  throw new errors_1.APIError({
354
372
  disableExternalErrorTracking: (0, errors_1.getDisableExternalErrorTracking)(error_4),
355
373
  error: error_4,
@@ -357,13 +375,13 @@ function modelRouter(model, options) {
357
375
  title: "postCreate hook error: ".concat(error_4.message),
358
376
  });
359
377
  case 17:
360
- _a.trys.push([17, 19, , 20]);
378
+ _b.trys.push([17, 19, , 20]);
361
379
  return [4 /*yield*/, responseHandler(data, "create", req, options)];
362
380
  case 18:
363
- serialized = _a.sent();
381
+ serialized = _b.sent();
364
382
  return [2 /*return*/, res.status(201).json({ data: serialized })];
365
383
  case 19:
366
- error_5 = _a.sent();
384
+ error_5 = _b.sent();
367
385
  throw new errors_1.APIError({
368
386
  disableExternalErrorTracking: (0, errors_1.getDisableExternalErrorTracking)(error_5),
369
387
  error: error_5,
@@ -376,15 +394,16 @@ function modelRouter(model, options) {
376
394
  // TODO add rate limit
377
395
  router.get("/", [
378
396
  (0, auth_1.authenticateMiddleware)(options.allowAnonymous),
379
- (0, permissions_1.permissionMiddleware)(model, options),
380
- (0, openApi_1.listOpenApiMiddleware)(model, options),
397
+ (0, permissions_1.permissionMiddleware)(baseModel, options),
398
+ (0, openApi_1.listOpenApiMiddleware)(baseModel, options),
381
399
  ], (0, exports.asyncHandler)(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
382
- var query, _a, _b, queryParam, _c, _d, queryParam, queryFilter, error_6, limit, builtQuery, total, populatedQuery, data, error_7, serialized, error_8, more, msg;
400
+ var model, query, _a, _b, queryParam, _c, _d, queryParam, queryFilter, error_6, limit, builtQuery, total, populatedQuery, data, error_7, serialized, error_8, more, msg;
383
401
  var e_4, _e, e_5, _f;
384
402
  var _g, _h, _j, _k, _l, _m;
385
403
  return __generator(this, function (_o) {
386
404
  switch (_o.label) {
387
405
  case 0:
406
+ model = baseModel;
388
407
  query = {};
389
408
  try {
390
409
  for (_a = __values(Object.keys((_g = options.defaultQueryParams) !== null && _g !== void 0 ? _g : [])), _b = _a.next(); !_b.done; _b = _a.next()) {
@@ -556,8 +575,8 @@ function modelRouter(model, options) {
556
575
  }); }));
557
576
  router.get("/:id", [
558
577
  (0, auth_1.authenticateMiddleware)(options.allowAnonymous),
559
- (0, openApi_1.getOpenApiMiddleware)(model, options),
560
- (0, permissions_1.permissionMiddleware)(model, options),
578
+ (0, openApi_1.getOpenApiMiddleware)(baseModel, options),
579
+ (0, permissions_1.permissionMiddleware)(baseModel, options),
561
580
  ], (0, exports.asyncHandler)(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
562
581
  var data, serialized, error_9;
563
582
  return __generator(this, function (_a) {
@@ -592,14 +611,15 @@ function modelRouter(model, options) {
592
611
  }); }));
593
612
  router.patch("/:id", [
594
613
  (0, auth_1.authenticateMiddleware)(options.allowAnonymous),
595
- (0, openApi_1.patchOpenApiMiddleware)(model, options),
596
- (0, permissions_1.permissionMiddleware)(model, options),
614
+ (0, openApi_1.patchOpenApiMiddleware)(baseModel, options),
615
+ (0, permissions_1.permissionMiddleware)(baseModel, options),
597
616
  ], (0, exports.asyncHandler)(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
598
- var doc, body, error_10, prevDoc, error_11, populateQuery, error_12, serialized, error_13;
617
+ var model, doc, body, error_10, prevDoc, error_11, populateQuery, error_12, serialized, error_13;
599
618
  var _a;
600
619
  return __generator(this, function (_b) {
601
620
  switch (_b.label) {
602
621
  case 0:
622
+ model = getModel(baseModel, req.body, options);
603
623
  doc = req.obj;
604
624
  try {
605
625
  body = (0, transformers_1.transform)(options, req.body, "update", req.user);
@@ -713,13 +733,14 @@ function modelRouter(model, options) {
713
733
  }); }));
714
734
  router.delete("/:id", [
715
735
  (0, auth_1.authenticateMiddleware)(options.allowAnonymous),
716
- (0, openApi_1.deleteOpenApiMiddleware)(model, options),
717
- (0, permissions_1.permissionMiddleware)(model, options),
736
+ (0, openApi_1.deleteOpenApiMiddleware)(baseModel, options),
737
+ (0, permissions_1.permissionMiddleware)(baseModel, options),
718
738
  ], (0, exports.asyncHandler)(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
719
- var doc, body, error_14, error_15, error_16;
739
+ var model, doc, body, error_14, error_15, error_16;
720
740
  return __generator(this, function (_a) {
721
741
  switch (_a.label) {
722
742
  case 0:
743
+ model = getModel(baseModel, req.body, options);
723
744
  doc = req.obj;
724
745
  if (!options.preDelete) return [3 /*break*/, 5];
725
746
  body = void 0;
@@ -802,14 +823,15 @@ function modelRouter(model, options) {
802
823
  }); }));
803
824
  function arrayOperation(req, res, operation) {
804
825
  return __awaiter(this, void 0, void 0, function () {
805
- var doc, prevDoc, field, array, index, body, error_17, error_18, error_19;
826
+ var model, doc, prevDoc, field, array, index, body, error_17, error_18, error_19;
806
827
  var _a;
807
828
  var _b, _c;
808
829
  return __generator(this, function (_d) {
809
830
  switch (_d.label) {
810
- case 0: return [4 /*yield*/, (0, permissions_1.checkPermissions)("update", options.permissions.update, req.user)];
831
+ case 0:
832
+ model = getModel(baseModel, req.body, options);
833
+ return [4 /*yield*/, (0, permissions_1.checkPermissions)("update", options.permissions.update, req.user)];
811
834
  case 1:
812
- // TODO Combine array operations and .patch(), as they are very similar.
813
835
  if (!(_d.sent())) {
814
836
  throw new errors_1.APIError({
815
837
  status: 405,
@@ -820,7 +842,9 @@ function modelRouter(model, options) {
820
842
  case 2:
821
843
  doc = _d.sent();
822
844
  prevDoc = (0, cloneDeep_1.default)(doc);
823
- if (!doc) {
845
+ // We fail here because we might fetch the document without the __t but we'd be missing all the
846
+ // hooks.
847
+ if (!doc || (doc.__t && !req.body.__t)) {
824
848
  throw new errors_1.APIError({
825
849
  status: 404,
826
850
  title: "Could not find document to PATCH: ".concat(req.params.id),
@@ -983,7 +1007,7 @@ function modelRouter(model, options) {
983
1007
  });
984
1008
  }
985
1009
  // Set up routes for managing array fields. Check if there any array fields to add this for.
986
- if (Object.values(model.schema.paths).find(function (config) { return config.instance === "Array"; })) {
1010
+ if (Object.values(baseModel.schema.paths).find(function (config) { return config.instance === "Array"; })) {
987
1011
  router.post("/:id/:field", (0, auth_1.authenticateMiddleware)(options.allowAnonymous), (0, exports.asyncHandler)(arrayPost));
988
1012
  router.patch("/:id/:field/:itemId", (0, auth_1.authenticateMiddleware)(options.allowAnonymous), (0, exports.asyncHandler)(arrayPatch));
989
1013
  router.delete("/:id/:field/:itemId", (0, auth_1.authenticateMiddleware)(options.allowAnonymous), (0, exports.asyncHandler)(arrayDelete));