@terreno/api 0.13.0 → 0.13.2

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.
@@ -86,9 +86,10 @@ export class GcpSecretProvider implements SecretProvider {
86
86
  const SecretManagerServiceClient =
87
87
  mod.SecretManagerServiceClient ?? mod.default?.SecretManagerServiceClient;
88
88
  if (!SecretManagerServiceClient) {
89
- throw new Error(
90
- "SecretManagerServiceClient not found in @google-cloud/secret-manager module"
91
- );
89
+ throw new APIError({
90
+ status: 500,
91
+ title: "SecretManagerServiceClient not found in @google-cloud/secret-manager module",
92
+ });
92
93
  }
93
94
  this.client = new SecretManagerServiceClient();
94
95
  }
package/src/tests.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import express, {type Express} from "express";
2
2
  import mongoose, {type Model, model, Schema} from "mongoose";
3
- import passportLocalMongoose from "passport-local-mongoose";
3
+ import passportLocalMongoose, {type PassportLocalMongooseDocument} from "passport-local-mongoose";
4
4
  import qs from "qs";
5
5
  import supertest from "supertest";
6
6
  import type TestAgent from "supertest/lib/agent";
@@ -62,19 +62,22 @@ const userSchema = new Schema<User>({
62
62
  username: {description: "The user's username", type: String},
63
63
  });
64
64
 
65
- userSchema.plugin(passportLocalMongoose as any, {
66
- attemptsField: "attempts",
67
- interval: process.env.NODE_ENV === "test" ? 1 : 100,
68
- limitAttempts: true,
69
- maxAttempts: 3,
70
- maxInterval: process.env.NODE_ENV === "test" ? 1 : 300000,
71
- usernameCaseInsensitive: true,
72
- usernameField: "email",
73
- });
65
+ userSchema.plugin(
66
+ passportLocalMongoose as unknown as (schema: Schema, options?: Record<string, unknown>) => void,
67
+ {
68
+ attemptsField: "attempts",
69
+ interval: process.env.NODE_ENV === "test" ? 1 : 100,
70
+ limitAttempts: true,
71
+ maxAttempts: 3,
72
+ maxInterval: process.env.NODE_ENV === "test" ? 1 : 300000,
73
+ usernameCaseInsensitive: true,
74
+ usernameField: "email",
75
+ }
76
+ );
74
77
  // userSchema.plugin(tokenPlugin);
75
78
  userSchema.plugin(createdUpdatedPlugin);
76
79
  userSchema.plugin(isDisabledPlugin);
77
- userSchema.methods.postCreate = async function (body: any) {
80
+ userSchema.methods.postCreate = async function (body: {age?: number}) {
78
81
  this.age = body.age;
79
82
  return this.save();
80
83
  };
@@ -121,6 +124,7 @@ const foodSchema = new Schema<Food>(
121
124
  type: Schema.Types.ObjectId,
122
125
  },
123
126
  ],
127
+ // noExplicitAny: DateOnly is a custom SchemaType not recognized by Mongoose's built-in type definitions
124
128
  expiration: {description: "Expiration date of the food", type: DateOnly as any},
125
129
  hidden: {
126
130
  default: false,
@@ -221,13 +225,13 @@ export const setupDb = async () => {
221
225
  UserModel.create({admin: true, email: "admin@example.com", name: "Admin"}),
222
226
  UserModel.create({admin: true, email: "admin+other@example.com", name: "Admin Other"}),
223
227
  ]);
224
- await (notAdmin as any).setPassword("password");
228
+ await (notAdmin as unknown as PassportLocalMongooseDocument).setPassword("password");
225
229
  await notAdmin.save();
226
230
 
227
- await (admin as any).setPassword("securePassword");
231
+ await (admin as unknown as PassportLocalMongooseDocument).setPassword("securePassword");
228
232
  await admin.save();
229
233
 
230
- await (adminOther as any).setPassword("otherPassword");
234
+ await (adminOther as unknown as PassportLocalMongooseDocument).setPassword("otherPassword");
231
235
 
232
236
  await adminOther.save();
233
237
 
@@ -15,7 +15,10 @@ export interface TerrenoTransformer<T> {
15
15
  serialize?: (obj: T, user?: User) => Partial<T> | undefined;
16
16
  }
17
17
 
18
- function getUserType(user?: User, obj?: any): "anon" | "auth" | "owner" | "admin" {
18
+ const getUserType = (
19
+ user?: User,
20
+ obj?: Record<string, unknown>
21
+ ): "anon" | "auth" | "owner" | "admin" => {
19
22
  if (user?.admin) {
20
23
  return "admin";
21
24
  }
@@ -26,10 +29,9 @@ function getUserType(user?: User, obj?: any): "anon" | "auth" | "owner" | "admin
26
29
  return "auth";
27
30
  }
28
31
  return "anon";
29
- }
32
+ };
30
33
 
31
- export function AdminOwnerTransformer<T>(options: {
32
- // TODO: do something with KeyOf here.
34
+ export const AdminOwnerTransformer = <T>(options: {
33
35
  anonReadFields?: string[];
34
36
  authReadFields?: string[];
35
37
  ownerReadFields?: string[];
@@ -38,20 +40,20 @@ export function AdminOwnerTransformer<T>(options: {
38
40
  authWriteFields?: string[];
39
41
  ownerWriteFields?: string[];
40
42
  adminWriteFields?: string[];
41
- }): TerrenoTransformer<T> {
42
- function pickFields(obj: Partial<T>, fields: any[]): Partial<T> {
43
+ }): TerrenoTransformer<T> => {
44
+ const pickFields = (obj: Partial<T>, fields: string[]): Partial<T> => {
43
45
  const newData: Partial<T> = {};
44
46
  for (const field of fields) {
45
- if (obj[field] !== undefined) {
46
- newData[field] = obj[field];
47
+ if ((obj as Record<string, unknown>)[field] !== undefined) {
48
+ (newData as Record<string, unknown>)[field] = (obj as Record<string, unknown>)[field];
47
49
  }
48
50
  }
49
51
  return newData;
50
- }
52
+ };
51
53
 
52
54
  return {
53
55
  serialize: (obj: T, user?: User) => {
54
- const userType = getUserType(user, obj);
56
+ const userType = getUserType(user, obj as Record<string, unknown>);
55
57
  if (userType === "admin") {
56
58
  return pickFields(obj, [...(options.adminReadFields ?? []), "id"]);
57
59
  }
@@ -63,10 +65,9 @@ export function AdminOwnerTransformer<T>(options: {
63
65
  }
64
66
  return pickFields(obj, [...(options.anonReadFields ?? []), "id"]);
65
67
  },
66
- // TODO: Migrate AdminOwnerTransform to use pre-hooks.
67
68
  transform: (obj: Partial<T>, _method: "create" | "update", user?: User) => {
68
- const userType = getUserType(user, obj);
69
- let allowedFields: any;
69
+ const userType = getUserType(user, obj as Record<string, unknown>);
70
+ let allowedFields: string[];
70
71
  if (userType === "admin") {
71
72
  allowedFields = options.adminWriteFields ?? [];
72
73
  } else if (userType === "owner") {
@@ -78,21 +79,22 @@ export function AdminOwnerTransformer<T>(options: {
78
79
  }
79
80
  const unallowedFields = Object.keys(obj).filter((k) => !allowedFields.includes(k));
80
81
  if (unallowedFields.length) {
81
- throw new Error(
82
- `User of type ${userType} cannot write fields: ${unallowedFields.join(", ")}`
83
- );
82
+ throw new APIError({
83
+ status: 403,
84
+ title: `User of type ${userType} cannot write fields: ${unallowedFields.join(", ")}`,
85
+ });
84
86
  }
85
87
  return obj;
86
88
  },
87
89
  };
88
- }
90
+ };
89
91
 
90
- export function transform<T>(
92
+ export const transform = <T>(
91
93
  options: ModelRouterOptions<T>,
92
94
  data: Partial<T> | Partial<T>[],
93
95
  method: "create" | "update",
94
96
  user?: User
95
- ) {
97
+ ) => {
96
98
  if (!options.transformer?.transform) {
97
99
  return data;
98
100
  }
@@ -108,16 +110,16 @@ export function transform<T>(
108
110
  return transformFn(data, method, user);
109
111
  }
110
112
  return data.map((d) => transformFn(d, method, user));
111
- }
113
+ };
112
114
 
113
- export function serialize<T>(
115
+ export const serialize = <T>(
114
116
  req: express.Request,
115
117
  options: ModelRouterOptions<T>,
116
- data: (Document<any, any, any> & T) | (Document<any, any, any> & T)[]
117
- ) {
118
- const serializeFn = (serializeData: Document<any, any, any> & T, serializeUser?: User) => {
118
+ data: (Document & T) | (Document & T)[]
119
+ ) => {
120
+ const serializeFn = (serializeData: Document & T, serializeUser?: User) => {
119
121
  const dataObject = serializeData.toObject() as T;
120
- (dataObject as any).id = serializeData._id;
122
+ (dataObject as Record<string, unknown>).id = serializeData._id;
121
123
 
122
124
  // Search for any value that is a Map and transform it to a plain object.
123
125
  // Otherwise Express drops the contents.
@@ -143,18 +145,18 @@ export function serialize<T>(
143
145
  return serializeFn(data, req.user);
144
146
  }
145
147
  return data.map((d) => serializeFn(d, req.user));
146
- }
148
+ };
147
149
 
148
150
  /**
149
151
  * Default response handler for modelRouter. Calls toObject on each doc and returns the result,
150
152
  * using transformers.serializer if provided.
151
153
  */
152
- export async function defaultResponseHandler<T>(
153
- doc: (Document<any, any, any> & T) | (Document<any, any, any> & T)[] | null,
154
+ export const defaultResponseHandler = async <T>(
155
+ doc: (Document & T) | (Document & T)[] | null,
154
156
  method: "list" | "create" | "read" | "update",
155
157
  request: express.Request,
156
158
  options: ModelRouterOptions<T>
157
- ) {
159
+ ) => {
158
160
  if (!doc) {
159
161
  return null;
160
162
  }
@@ -168,4 +170,4 @@ export async function defaultResponseHandler<T>(
168
170
  title: `Error serializing ${method} response: ${errorObj.message}`,
169
171
  });
170
172
  }
171
- }
173
+ };
package/src/utils.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import mongoose, {Types} from "mongoose";
2
2
 
3
+ import {APIError} from "./errors";
3
4
  import {logger} from "./logger";
4
5
 
5
6
  // A better version of mongoose's ObjectId.isValid,
@@ -31,17 +32,26 @@ export const checkModelsStrict = (ignoredModels: string[] = []): void => {
31
32
  const schema = mongoose.model(model).schema;
32
33
 
33
34
  if (schema.get("toObject")?.virtuals !== true) {
34
- throw new Error(`Model ${model} toObject.virtuals not set to true`);
35
+ throw new APIError({
36
+ status: 500,
37
+ title: `Model ${model} toObject.virtuals not set to true`,
38
+ });
35
39
  }
36
40
  if (schema.get("toJSON")?.virtuals !== true) {
37
- throw new Error(`Model ${model} toJSON.virtuals not set to true`);
41
+ throw new APIError({
42
+ status: 500,
43
+ title: `Model ${model} toJSON.virtuals not set to true`,
44
+ });
38
45
  }
39
46
 
40
47
  if (ignoredModels.includes(model)) {
41
48
  continue;
42
49
  }
43
50
  if (schema.get("strict") !== "throw") {
44
- throw new Error(`Model ${model} is not set to strict mode.`);
51
+ throw new APIError({
52
+ status: 500,
53
+ title: `Model ${model} is not set to strict mode.`,
54
+ });
45
55
  }
46
56
  }
47
57
  };