fastify-txstate 3.2.5 → 3.2.7

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/lib/error.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type FastifySchemaValidationError } from 'fastify/types/schema';
1
2
  export declare class HttpError extends Error {
2
3
  statusCode: number;
3
4
  constructor(statusCode: number, message?: string);
@@ -7,4 +8,9 @@ export declare class FailedValidationError extends HttpError {
7
8
  errors: ValidationErrors;
8
9
  constructor(errors: ValidationErrors);
9
10
  }
11
+ export declare function fstValidationToMessage(v: FastifySchemaValidationError): {
12
+ message: string | undefined;
13
+ path: string;
14
+ type: string;
15
+ };
10
16
  export {};
package/lib/error.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FailedValidationError = exports.HttpError = void 0;
3
+ exports.fstValidationToMessage = exports.FailedValidationError = exports.HttpError = void 0;
4
4
  const http_status_codes_1 = require("http-status-codes");
5
5
  class HttpError extends Error {
6
6
  constructor(statusCode, message) {
@@ -24,3 +24,8 @@ class FailedValidationError extends HttpError {
24
24
  }
25
25
  }
26
26
  exports.FailedValidationError = FailedValidationError;
27
+ function fstValidationToMessage(v) {
28
+ const instancePath = v.keyword === 'required' ? v.instancePath + '/' + v.params.missingProperty : v.instancePath;
29
+ return { message: v.message, path: instancePath.substring(1).replace(/\//g, '.'), type: 'error' };
30
+ }
31
+ exports.fstValidationToMessage = fstValidationToMessage;
package/lib/index.js CHANGED
@@ -20,6 +20,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
20
20
  exports.prodLogger = exports.devLogger = void 0;
21
21
  const swagger_1 = __importDefault(require("@fastify/swagger"));
22
22
  const swagger_ui_1 = __importDefault(require("@fastify/swagger-ui"));
23
+ const fastify_shared_1 = require("@txstate-mws/fastify-shared");
23
24
  const ajv_errors_1 = __importDefault(require("ajv-errors"));
24
25
  const ajv_formats_1 = __importDefault(require("ajv-formats"));
25
26
  const fastify_1 = require("fastify");
@@ -108,25 +109,10 @@ class Server {
108
109
  if (!((_a = route.schema) === null || _a === void 0 ? void 0 : _a.body))
109
110
  return;
110
111
  const missingResponse = ((_b = route.schema) === null || _b === void 0 ? void 0 : _b.response) == null;
111
- const newSchema = (0, txstate_utils_1.set)((_c = route.schema) !== null && _c !== void 0 ? _c : {}, 'response.422', {
112
- description: 'Validation failure.',
113
- type: 'object',
114
- properties: {
115
- errors: {
116
- type: 'array',
117
- items: {
118
- type: 'object',
119
- properties: {
120
- type: { type: 'string', enum: ['error', 'warning', 'success', 'system'], example: 'error' },
121
- message: { type: 'string', example: 'must be an integer' },
122
- path: { type: 'string', example: 'cart.item.0.quantity', description: 'Dot-separated path to the field in the request body that caused the validation error.' }
123
- },
124
- required: ['type', 'message'],
125
- additionalProperties: false
126
- }
127
- }
128
- }
129
- });
112
+ const response400 = (0, txstate_utils_1.set)(fastify_shared_1.validatedResponse.properties.messages, 'description', 'Basic validation failure. This means that the UI provided input that failed validation as defined in the openapi specification published by the API. The UI is at fault and should be re-coded to avoid sending invalid data.');
113
+ let newSchema = (0, txstate_utils_1.set)((_c = route.schema) !== null && _c !== void 0 ? _c : {}, 'response.400', response400);
114
+ const response422 = (0, txstate_utils_1.set)(fastify_shared_1.validatedResponse, 'description', 'Validation failure. This means that the user provided an invalid object. The user should be shown their error so that they can correct it.');
115
+ newSchema = (0, txstate_utils_1.set)(newSchema, 'response.422', response422);
130
116
  if (missingResponse) {
131
117
  newSchema.response['200'] = {
132
118
  description: 'Success. Return type has not been specified.',
@@ -213,7 +199,7 @@ class Server {
213
199
  });
214
200
  this.app.setNotFoundHandler((req, res) => { void res.status(404).send('Not Found.'); });
215
201
  this.app.setErrorHandler(async (err, req, res) => {
216
- var _a, _b;
202
+ var _a;
217
203
  req.log.warn(err);
218
204
  for (const errorHandler of this.errorHandlers) {
219
205
  if (!res.sent)
@@ -227,7 +213,28 @@ class Server {
227
213
  await res.status(err.statusCode).send(err.message);
228
214
  }
229
215
  else if (err.code === 'FST_ERR_VALIDATION') {
230
- await res.status(422).send({ errors: (_b = (_a = err.validation) === null || _a === void 0 ? void 0 : _a.map(err => ({ message: err.message, path: err.instancePath.substring(1).replace(/\//g, '.'), type: 'error' }))) !== null && _b !== void 0 ? _b : [] });
216
+ const developerErrors = [];
217
+ const userErrors = [];
218
+ for (const v of (_a = err.validation) !== null && _a !== void 0 ? _a : []) {
219
+ if (v.keyword === 'errorMessage') {
220
+ for (const ov of v.params.errors) {
221
+ if (['type', 'additionalProperties'].includes(ov.keyword))
222
+ developerErrors.push({ ...ov, message: v.message });
223
+ else
224
+ userErrors.push({ ...ov, message: v.message });
225
+ }
226
+ }
227
+ else {
228
+ if (['type', 'additionalProperties'].includes(v.keyword))
229
+ developerErrors.push(v);
230
+ else
231
+ userErrors.push(v);
232
+ }
233
+ }
234
+ if (userErrors.length)
235
+ await res.status(422).send({ success: false, messages: userErrors.map(error_1.fstValidationToMessage) });
236
+ else
237
+ await res.status(400).send(developerErrors.map(error_1.fstValidationToMessage));
231
238
  }
232
239
  else if (err.statusCode) {
233
240
  await res.status(err.statusCode).send(new error_1.HttpError(err.statusCode).message);
@@ -29,6 +29,7 @@ declare class Context<AuthType extends FastifyTxStateAuthInfo = FastifyTxStateAu
29
29
  }>;
30
30
  constructor(req?: FastifyRequest);
31
31
  waitForAuth(): Promise<AuthType | undefined>;
32
+ protected static hasInitialized: boolean;
32
33
  static init(): void;
33
34
  /**
34
35
  * If implemented, this method will be called on startup, once per configured issuer. It receives
@@ -58,5 +59,5 @@ declare class TxStateUAuthContext extends Context {
58
59
  authFromPayload(payload: JWTPayload): Promise<FastifyTxStateAuthInfo>;
59
60
  }
60
61
  export declare function unifiedAuthenticate(req: FastifyRequest, ContextClass?: typeof TxStateUAuthContext): Promise<FastifyTxStateAuthInfo | undefined>;
61
- export declare function unifiedAuthenticateAll(req: FastifyRequest, ContextClass?: typeof TxStateUAuthContext): Promise<FastifyTxStateAuthInfo>;
62
+ export declare function unifiedAuthenticateAll(req: FastifyRequest, ContextClass?: typeof TxStateUAuthContext): Promise<FastifyTxStateAuthInfo | undefined>;
62
63
  export {};
@@ -28,6 +28,9 @@ class Context extends MockContext {
28
28
  }
29
29
  static init() {
30
30
  var _b;
31
+ if (this.hasInitialized)
32
+ return;
33
+ this.hasInitialized = true;
31
34
  let secret = cleanPem(process.env.JWT_SECRET_VERIFY);
32
35
  if (secret != null) {
33
36
  _a.jwtVerifyKey = (0, crypto_1.createPublicKey)(secret);
@@ -102,6 +105,7 @@ Context.tokenCache = new txstate_utils_1.Cache(async (token, { req, ctx }) => {
102
105
  return undefined;
103
106
  }
104
107
  }, { freshseconds: 10 });
108
+ Context.hasInitialized = false;
105
109
  class TxStateUAuthContext extends Context {
106
110
  static processIssuerConfig(config) {
107
111
  var _b;
@@ -132,14 +136,17 @@ class TxStateUAuthContext extends Context {
132
136
  }
133
137
  }
134
138
  async function unifiedAuthenticate(req, ContextClass = TxStateUAuthContext) {
139
+ ContextClass.init();
135
140
  const ctx = new ContextClass(req);
136
141
  return ctx.waitForAuth();
137
142
  }
138
143
  exports.unifiedAuthenticate = unifiedAuthenticate;
139
144
  async function unifiedAuthenticateAll(req, ContextClass = TxStateUAuthContext) {
145
+ var _b;
146
+ ContextClass.init();
140
147
  const ctx = new ContextClass(req);
141
148
  const auth = await ctx.waitForAuth();
142
- if (!(auth === null || auth === void 0 ? void 0 : auth.username.length))
149
+ if (!(auth === null || auth === void 0 ? void 0 : auth.username.length) && !((_b = req.routeOptions.url) === null || _b === void 0 ? void 0 : _b.startsWith('/docs')))
143
150
  throw new Error('All requests require authentication.');
144
151
  return auth;
145
152
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify-txstate",
3
- "version": "3.2.5",
3
+ "version": "3.2.7",
4
4
  "description": "A small wrapper for fastify providing a set of common conventions & utility functions we use.",
5
5
  "exports": {
6
6
  ".": {
@@ -22,7 +22,7 @@
22
22
  "@fastify/swagger": "^8.14.0",
23
23
  "@fastify/swagger-ui": "^3.0.0",
24
24
  "@fastify/type-provider-json-schema-to-ts": "^3.0.0",
25
- "@txstate-mws/fastify-shared": "^1.0.4",
25
+ "@txstate-mws/fastify-shared": "^1.0.9",
26
26
  "ajv-errors": "^3.0.0",
27
27
  "ajv-formats": "^2.1.1",
28
28
  "fastify": "^4.9.2",