@fjell/express-router 4.4.1 → 4.4.3

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/src/ItemRouter.ts CHANGED
@@ -11,25 +11,11 @@ import {
11
11
  } from "@fjell/core";
12
12
  import { NotFoundError, Operations } from "@fjell/lib";
13
13
  import deepmerge from "deepmerge";
14
- import { Request, RequestHandler, Response, Router } from "express";
14
+ import { Request, Response, Router } from "express";
15
15
  import LibLogger from "./logger";
16
16
 
17
17
  export type ItemRouterOptions = Record<string, never>;
18
18
 
19
- // TODO: body is in the request, it's not needed in the parameters
20
- export type ActionMethod = <
21
- S extends string,
22
- L1 extends string = never,
23
- L2 extends string = never,
24
- L3 extends string = never,
25
- L4 extends string = never,
26
- L5 extends string = never
27
- >(req: Request, res: Response, item: Item<S, L1, L2, L3, L4, L5>, params: any, body: any) =>
28
- Promise<Item<S, L1, L2, L3, L4, L5>>;
29
-
30
- // TODO: body is in the request, it's not needed in the parameters
31
- export type AllActionMethods = Array<RequestHandler>;
32
-
33
19
  export class ItemRouter<
34
20
  S extends string,
35
21
  L1 extends string = never,
@@ -44,7 +30,6 @@ export class ItemRouter<
44
30
  protected options: ItemRouterOptions;
45
31
  private childRouters: Record<string, Router> = {};
46
32
  private logger;
47
- private itemActions: Record<string, ActionMethod> | undefined;
48
33
 
49
34
  constructor(
50
35
  lib: Operations<Item<S, L1, L2, L3, L4, L5>, S, L1, L2, L3, L4, L5>,
@@ -94,17 +79,45 @@ export class ItemRouter<
94
79
  this.logger.default('Getting Item', { query: req?.query, params: req?.params, locals: res?.locals });
95
80
  const ik = this.getIk(res);
96
81
  const actionKey = req.path.substring(req.path.lastIndexOf('/') + 1);
97
- if (!this.itemActions) {
82
+ if (!this.lib.actions) {
98
83
  this.logger.error('Item Actions are not configured');
99
- return res.status(500).json({ error: 'Item Actions are not configured' });
84
+ res.status(500).json({ error: 'Item Actions are not configured' });
85
+ return;
86
+ }
87
+ const action = this.lib.actions[actionKey];
88
+ if (!action) {
89
+ this.logger.error('Item Action is not configured', { actionKey });
90
+ res.status(500).json({ error: 'Item Action is not configured' });
91
+ return;
100
92
  }
101
93
  try {
102
- const item =
103
- validatePK(await this.lib.get(ik), this.getPkType()) as Item<S, L1, L2, L3, L4, L5>;
104
- return res.json(await this.itemActions[actionKey](req, res, item, req.params, req.body));
94
+ res.json(await this.lib.action(ik, actionKey, req.body));
105
95
  } catch (err: any) {
106
96
  this.logger.error('Error in Item Action', { message: err?.message, stack: err?.stack });
107
- return res.status(500).json(err);
97
+ res.status(500).json(err);
98
+ }
99
+ }
100
+
101
+ protected getItemFacet = async (req: Request, res: Response) => {
102
+ this.logger.default('Getting Item', { query: req?.query, params: req?.params, locals: res?.locals });
103
+ const ik = this.getIk(res);
104
+ const facetKey = req.path.substring(req.path.lastIndexOf('/') + 1);
105
+ if (!this.lib.facets) {
106
+ this.logger.error('Item Facets are not configured');
107
+ res.status(500).json({ error: 'Item Facets are not configured' });
108
+ return;
109
+ }
110
+ const facet = this.lib.facets[facetKey];
111
+ if (!facet) {
112
+ this.logger.error('Item Facet is not configured', { facetKey });
113
+ res.status(500).json({ error: 'Item Facet is not configured' });
114
+ return;
115
+ }
116
+ try {
117
+ res.json(await this.lib.facet(ik, facetKey, req.params));
118
+ } catch (err: any) {
119
+ this.logger.error('Error in Item Facet', { message: err?.message, stack: err?.stack });
120
+ res.status(500).json(err);
108
121
  }
109
122
  }
110
123
 
@@ -113,31 +126,39 @@ export class ItemRouter<
113
126
  router.get('/', this.findItems);
114
127
  router.post('/', this.createItem);
115
128
 
116
- const allActions = this.configureAllActions();
117
- this.logger.debug('All Actions supplied to Router', { allActions });
118
- if (allActions) {
119
- Object.keys(allActions).forEach((actionKey) => {
120
- this.logger.default('Configuring All Action', { actionKey });
121
- // TODO: Ok, this is a bit of a hack, but we need to customize the types of the request handlers
122
- router.post(`/${actionKey}`, ...allActions[actionKey]);
123
- });
124
- }
129
+ // const allActions = this.configureAllActions();
130
+ // this.logger.debug('All Actions supplied to Router', { allActions });
131
+ // if (allActions) {
132
+ // Object.keys(allActions).forEach((actionKey) => {
133
+ // this.logger.default('Configuring All Action', { actionKey });
134
+ // // TODO: Ok, this is a bit of a hack, but we need to customize the types of the request handlers
135
+ // router.post(`/${actionKey}`, ...allActions[actionKey]);
136
+ // });
137
+ // }
125
138
 
126
139
  const itemRouter = Router();
127
140
  itemRouter.get('/', this.getItem);
128
141
  itemRouter.put('/', this.updateItem);
129
142
  itemRouter.delete('/', this.deleteItem);
130
143
 
131
- this.itemActions = this.configureItemActions();
132
- this.logger.debug('Item Actions supplied to Router', { itemActions: this.itemActions });
133
- if (this.itemActions) {
134
- Object.keys(this.itemActions).forEach((actionKey) => {
144
+ this.logger.debug('Item Actions supplied to Router', { itemActions: this.lib.actions });
145
+ if (this.lib.actions) {
146
+ Object.keys(this.lib.actions).forEach((actionKey) => {
135
147
  this.logger.default('Configuring Item Action', { actionKey });
136
148
  // TODO: Ok, this is a bit of a hack, but we need to customize the types of the request handlers
137
149
  itemRouter.post(`/${actionKey}`, this.postItemAction)
138
150
  });
139
151
  }
140
152
 
153
+ this.logger.debug('Item Facets supplied to Router', { itemFacets: this.lib.facets });
154
+ if (this.lib.facets) {
155
+ Object.keys(this.lib.facets).forEach((facetKey) => {
156
+ this.logger.default('Configuring Item Facet', { facetKey });
157
+ // TODO: Ok, this is a bit of a hack, but we need to customize the types of the request handlers
158
+ itemRouter.get(`/${facetKey}`, this.getItemFacet)
159
+ });
160
+ }
161
+
141
162
  this.logger.default('Configuring Item Operations under PK Param', { pkParam: this.getPkParam() });
142
163
  router.use(`/:${this.getPkParam()}`, this.validatePrimaryKeyValue, itemRouter);
143
164
 
@@ -171,18 +192,6 @@ export class ItemRouter<
171
192
  this.childRouters[path] = router;
172
193
  }
173
194
 
174
- /* istanbul ignore next */
175
- protected configureItemActions(): Record<string, ActionMethod> {
176
- this.logger.debug('ARouter - No Item Actions Configured');
177
- return {};
178
- }
179
-
180
- /* istanbul ignore next */
181
- protected configureAllActions(): Record<string, AllActionMethods> {
182
- this.logger.debug('ARouter - No All Actions Configured');
183
- return {};
184
- }
185
-
186
195
  /* istanbul ignore next */
187
196
  public getRouter(): Router {
188
197
  const router = Router();
@@ -192,7 +201,7 @@ export class ItemRouter<
192
201
 
193
202
  /* istanbul ignore next */
194
203
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
195
- protected createItem = async (req: Request, res: Response): Promise<Response<any, Record<string, any>>> => {
204
+ protected createItem = async (req: Request, res: Response): Promise<void> => {
196
205
  throw new Error('Method not implemented in an abstract router');
197
206
  };
198
207
 
@@ -203,17 +212,17 @@ export class ItemRouter<
203
212
  return item;
204
213
  };
205
214
 
206
- protected deleteItem = async (req: Request, res: Response): Promise<Response<any, Record<string, any>>> => {
215
+ protected deleteItem = async (req: Request, res: Response): Promise<void> => {
207
216
  this.logger.default('Deleting Item', { query: req.query, params: req.params, locals: res.locals });
208
217
  const ik = this.getIk(res);
209
218
  const removedItem = await this.lib.remove(ik);
210
219
  const item = validatePK(removedItem, this.getPkType());
211
- return res.json(item);
220
+ res.json(item);
212
221
  };
213
222
 
214
223
  /* eslint-disable */
215
224
  /* istanbul ignore next */
216
- protected findItems = async (req: Request, res: Response): Promise<Response<any, Record<string, any>>> => {
225
+ protected findItems = async (req: Request, res: Response): Promise<void> => {
217
226
  throw new Error('Method not implemented in an abstract router');
218
227
  };
219
228
  /* eslint-enable */
@@ -224,17 +233,17 @@ export class ItemRouter<
224
233
  try {
225
234
  // TODO: What error does validate PK throw, when can that fail?
226
235
  const item = validatePK(await this.lib.get(ik), this.getPkType());
227
- return res.json(item);
236
+ res.json(item);
228
237
  } catch (err: any) {
229
238
  if (err instanceof NotFoundError) {
230
239
  this.logger.error('Item Not Found', { ik, message: err?.message, stack: err?.stack });
231
- return res.status(404).json({
240
+ res.status(404).json({
232
241
  ik,
233
242
  message: "Item Not Found",
234
243
  });
235
244
  } else {
236
245
  this.logger.error('General Error', { ik, message: err?.message, stack: err?.stack });
237
- return res.status(500).json({
246
+ res.status(500).json({
238
247
  ik,
239
248
  message: "General Error",
240
249
  });
@@ -248,7 +257,7 @@ export class ItemRouter<
248
257
  const ik = this.getIk(res);
249
258
  const itemToUpdate = this.convertDates(req.body as ItemProperties<S, L1, L2, L3, L4, L5>);
250
259
  const retItem = validatePK(await this.lib.update(ik, itemToUpdate), this.getPkType());
251
- return res.json(retItem);
260
+ res.json(retItem);
252
261
  };
253
262
 
254
263
  public convertDates = (item: Item<S, L1, L2, L3, L4, L5> | ItemProperties<S, L1, L2, L3, L4, L5>):
@@ -13,7 +13,7 @@ interface ParsedQuery {
13
13
  export class PItemRouter<T extends Item<S>, S extends string> extends ItemRouter<S> {
14
14
 
15
15
  constructor(lib: Primary.Operations<T, S>, keyType: S, options: ItemRouterOptions = {}) {
16
- super(lib, keyType, options);
16
+ super(lib as any, keyType, options);
17
17
  }
18
18
 
19
19
  public getIk(res: Response): PriKey<S> {
@@ -27,10 +27,9 @@ export class PItemRouter<T extends Item<S>, S extends string> extends ItemRouter
27
27
  let item =
28
28
  validatePK(await this.lib.create(itemToCreate), this.getPkType()) as Item<S>;
29
29
  item = await this.postCreateItem(item);
30
- return res.json(item);
30
+ res.json(item);
31
31
  };
32
32
 
33
- /* eslint-disable */
34
33
  protected findItems = async (req: Request, res: Response) => {
35
34
  logger.default('Finding Items', { query: req.query, params: req.params, locals: res.locals });
36
35
 
@@ -39,11 +38,18 @@ export class PItemRouter<T extends Item<S>, S extends string> extends ItemRouter
39
38
  const query: ParsedQuery = req.query as unknown as ParsedQuery;
40
39
  const finder = query['finder'] as string;
41
40
  const finderParams = query['finderParams'] as string;
41
+ const one = query['one'] as string;
42
42
 
43
- if( finder ) {
43
+ if (finder) {
44
44
  // If finder is defined? Call a finder.
45
- logger.default('Finding Items with a finder', { finder, finderParams });
46
- items = await this.lib.find(finder, JSON.parse(finderParams));
45
+ logger.default('Finding Items with a finder', { finder, finderParams, one });
46
+
47
+ if (one === 'true') {
48
+ const item = await (this.lib as any).findOne(finder, JSON.parse(finderParams));
49
+ items = item ? [item] : [];
50
+ } else {
51
+ items = await this.lib.find(finder, JSON.parse(finderParams));
52
+ }
47
53
  } else {
48
54
  logger.default('Finding Items with a query', { query: req.query });
49
55
  // TODO: This is once of the more important places to perform some validaation and feedback
@@ -51,7 +57,7 @@ export class PItemRouter<T extends Item<S>, S extends string> extends ItemRouter
51
57
  items = await this.lib.all(itemQuery);
52
58
  }
53
59
 
54
- return res.json(items.map((item: Item<S>) => validatePK(item, this.getPkType())));
60
+ res.json(items.map((item: Item<S>) => validatePK(item, this.getPkType())));
55
61
  };
56
- /* eslint-enable */
62
+
57
63
  }