@nymphjs/server 1.0.0-beta.10 → 1.0.0-beta.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/src/index.ts CHANGED
@@ -18,6 +18,10 @@ import {
18
18
  } from '@nymphjs/nymph';
19
19
  import { EntityInvalidDataError } from '@nymphjs/nymph';
20
20
 
21
+ import { HttpError } from './HttpError';
22
+
23
+ export { HttpError };
24
+
21
25
  type NymphResponse = Response<any, { nymph: Nymph }>;
22
26
 
23
27
  const NOT_FOUND_ERROR = 'Entity is not found.';
@@ -59,7 +63,7 @@ export default function createServer(
59
63
  try {
60
64
  response.locals.nymph.tilmeld.authenticate();
61
65
  } catch (e: any) {
62
- httpError(response, 500, 'Internal Server Error', e);
66
+ httpError(response, 500, e);
63
67
  return;
64
68
  }
65
69
  }
@@ -77,7 +81,7 @@ export default function createServer(
77
81
  try {
78
82
  response.locals.nymph.tilmeld.clearSession();
79
83
  } catch (e: any) {
80
- httpError(response, 500, 'Internal Server Error', e);
84
+ httpError(response, 500, e);
81
85
  return;
82
86
  }
83
87
  }
@@ -117,21 +121,21 @@ export default function createServer(
117
121
  try {
118
122
  const { action, data } = getActionData(request);
119
123
  if (['entity', 'entities', 'uid'].indexOf(action) === -1) {
120
- httpError(response, 400, 'Bad Request');
124
+ httpError(response, 400);
121
125
  return;
122
126
  }
123
127
  if (['entity', 'entities'].indexOf(action) !== -1) {
124
128
  if (!Array.isArray(data)) {
125
- httpError(response, 400, 'Bad Request');
129
+ httpError(response, 400);
126
130
  return;
127
131
  }
128
132
  const count = data.length;
129
133
  if (count < 1 || typeof data[0] !== 'object') {
130
- httpError(response, 400, 'Bad Request');
134
+ httpError(response, 400);
131
135
  return;
132
136
  }
133
137
  if (!('class' in data[0])) {
134
- httpError(response, 400, 'Bad Request');
138
+ httpError(response, 400);
135
139
  return;
136
140
  }
137
141
  let [options, ...selectors] = data as [Options, ...Selector[]];
@@ -139,7 +143,7 @@ export default function createServer(
139
143
  try {
140
144
  EntityClass = response.locals.nymph.getEntityClass(data[0].class);
141
145
  } catch (e: any) {
142
- httpError(response, 400, 'Bad Request', e);
146
+ httpError(response, 400, e);
143
147
  return;
144
148
  }
145
149
  options.class = EntityClass;
@@ -169,7 +173,7 @@ export default function createServer(
169
173
  );
170
174
  }
171
175
  } catch (e: any) {
172
- httpError(response, 500, 'Internal Server Error', e);
176
+ httpError(response, 500, e);
173
177
  return;
174
178
  }
175
179
  if (result == null || (Array.isArray(result) && result.length === 0)) {
@@ -177,7 +181,7 @@ export default function createServer(
177
181
  action === 'entity' ||
178
182
  response.locals.nymph.config.emptyListError
179
183
  ) {
180
- httpError(response, 404, 'Not Found');
184
+ httpError(response, 404);
181
185
  return;
182
186
  }
183
187
  }
@@ -185,7 +189,7 @@ export default function createServer(
185
189
  response.send(JSON.stringify(result));
186
190
  } else {
187
191
  if (typeof data !== 'string') {
188
- httpError(response, 400, 'Bad Request');
192
+ httpError(response, 400);
189
193
  return;
190
194
  }
191
195
  if (response.locals.nymph.tilmeld) {
@@ -195,7 +199,7 @@ export default function createServer(
195
199
  TilmeldAccessLevels.READ_ACCESS
196
200
  )
197
201
  ) {
198
- httpError(response, 403, 'Forbidden');
202
+ httpError(response, 403);
199
203
  return;
200
204
  }
201
205
  }
@@ -203,21 +207,21 @@ export default function createServer(
203
207
  try {
204
208
  result = await response.locals.nymph.getUID(data);
205
209
  } catch (e: any) {
206
- httpError(response, 500, 'Internal Server Error', e);
210
+ httpError(response, 500, e);
207
211
  return;
208
212
  }
209
213
  if (result === null) {
210
- httpError(response, 404, 'Not Found');
214
+ httpError(response, 404);
211
215
  return;
212
216
  } else if (typeof result !== 'number') {
213
- httpError(response, 500, 'Internal Server Error');
217
+ httpError(response, 500);
214
218
  return;
215
219
  }
216
220
  response.setHeader('Content-Type', 'text/plain');
217
221
  response.send(`${result}`);
218
222
  }
219
223
  } catch (e: any) {
220
- httpError(response, 500, 'Internal Server Error', e);
224
+ httpError(response, 500, e);
221
225
  return;
222
226
  }
223
227
  });
@@ -227,7 +231,7 @@ export default function createServer(
227
231
  const { action, data: dataConst } = getActionData(request);
228
232
  let data = dataConst;
229
233
  if (['entity', 'entities', 'uid', 'method'].indexOf(action) === -1) {
230
- httpError(response, 400, 'Bad Request');
234
+ httpError(response, 400);
231
235
  return;
232
236
  }
233
237
  if (['entity', 'entities'].indexOf(action) !== -1) {
@@ -286,16 +290,16 @@ export default function createServer(
286
290
  }
287
291
  if (!hadSuccess) {
288
292
  if (invalidRequest) {
289
- httpError(response, 400, 'Bad Request', lastException);
293
+ httpError(response, 400, lastException);
290
294
  return;
291
295
  } else if (conflict) {
292
- httpError(response, 409, 'Conflict');
296
+ httpError(response, 409);
293
297
  return;
294
298
  } else if (notfound) {
295
- httpError(response, 404, 'Not Found');
299
+ httpError(response, 404);
296
300
  return;
297
301
  } else {
298
- httpError(response, 500, 'Internal Server Error', lastException);
302
+ httpError(response, 500, lastException);
299
303
  return;
300
304
  }
301
305
  }
@@ -308,7 +312,7 @@ export default function createServer(
308
312
  }
309
313
  } else if (action === 'method') {
310
314
  if (!Array.isArray(data.params)) {
311
- httpError(response, 400, 'Bad Request');
315
+ httpError(response, 400);
312
316
  return;
313
317
  }
314
318
  const params = referencesToEntities(
@@ -320,23 +324,23 @@ export default function createServer(
320
324
  try {
321
325
  EntityClass = response.locals.nymph.getEntityClass(data.class);
322
326
  } catch (e: any) {
323
- httpError(response, 400, 'Bad Request');
327
+ httpError(response, 400);
324
328
  return;
325
329
  }
326
330
  if (
327
331
  EntityClass.clientEnabledStaticMethods.indexOf(data.method) === -1
328
332
  ) {
329
- httpError(response, 403, 'Forbidden');
333
+ httpError(response, 403);
330
334
  return;
331
335
  }
332
336
  if (!(data.method in EntityClass)) {
333
- httpError(response, 400, 'Bad Request');
337
+ httpError(response, 400);
334
338
  return;
335
339
  }
336
340
  // @ts-ignore Dynamic methods make TypeScript sad.
337
341
  const method: Function = EntityClass[data.method];
338
342
  if (typeof method !== 'function') {
339
- httpError(response, 400, 'Bad Request');
343
+ httpError(response, 400);
340
344
  return;
341
345
  }
342
346
  try {
@@ -349,7 +353,7 @@ export default function createServer(
349
353
  response.setHeader('Content-Type', 'application/json');
350
354
  response.send({ return: ret });
351
355
  } catch (e: any) {
352
- httpError(response, 500, 'Internal Server Error', e);
356
+ httpError(response, 500, e);
353
357
  return;
354
358
  }
355
359
  } else {
@@ -358,29 +362,29 @@ export default function createServer(
358
362
  entity = await loadEntity(data.entity, response.locals.nymph);
359
363
  } catch (e: any) {
360
364
  if (e instanceof EntityConflictError) {
361
- httpError(response, 409, 'Conflict');
365
+ httpError(response, 409);
362
366
  } else if (e.message === NOT_FOUND_ERROR) {
363
- httpError(response, 404, 'Not Found', e);
367
+ httpError(response, 404, e);
364
368
  } else if (e instanceof InvalidParametersError) {
365
- httpError(response, 400, 'Bad Request', e);
369
+ httpError(response, 400, e);
366
370
  } else {
367
- httpError(response, 500, 'Internal Server Error', e);
371
+ httpError(response, 500, e);
368
372
  }
369
373
  return;
370
374
  }
371
375
  if (data.entity.guid && !entity.guid) {
372
- httpError(response, 400, 'Bad Request');
376
+ httpError(response, 400);
373
377
  return;
374
378
  }
375
379
  if (entity.$getClientEnabledMethods().indexOf(data.method) === -1) {
376
- httpError(response, 403, 'Forbidden');
380
+ httpError(response, 403);
377
381
  return;
378
382
  }
379
383
  if (
380
384
  !(data.method in entity) ||
381
385
  typeof entity[data.method] !== 'function'
382
386
  ) {
383
- httpError(response, 400, 'Bad Request');
387
+ httpError(response, 400);
384
388
  return;
385
389
  }
386
390
  try {
@@ -397,13 +401,13 @@ export default function createServer(
397
401
  response.send({ entity: entity, return: ret });
398
402
  }
399
403
  } catch (e: any) {
400
- httpError(response, 500, 'Internal Server Error', e);
404
+ httpError(response, 500, e);
401
405
  return;
402
406
  }
403
407
  }
404
408
  } else {
405
409
  if (typeof data !== 'string') {
406
- httpError(response, 400, 'Bad Request');
410
+ httpError(response, 400);
407
411
  return;
408
412
  }
409
413
  if (response.locals.nymph.tilmeld) {
@@ -413,7 +417,7 @@ export default function createServer(
413
417
  TilmeldAccessLevels.WRITE_ACCESS
414
418
  )
415
419
  ) {
416
- httpError(response, 403, 'Forbidden');
420
+ httpError(response, 403);
417
421
  return;
418
422
  }
419
423
  }
@@ -421,11 +425,11 @@ export default function createServer(
421
425
  try {
422
426
  result = await response.locals.nymph.newUID(data);
423
427
  } catch (e: any) {
424
- httpError(response, 500, 'Internal Server Error', e);
428
+ httpError(response, 500, e);
425
429
  return;
426
430
  }
427
431
  if (typeof result !== 'number') {
428
- httpError(response, 500, 'Internal Server Error');
432
+ httpError(response, 500);
429
433
  return;
430
434
  }
431
435
  response.status(201);
@@ -433,7 +437,7 @@ export default function createServer(
433
437
  response.send(`${result}`);
434
438
  }
435
439
  } catch (e: any) {
436
- httpError(response, 500, 'Internal Server Error', e);
440
+ httpError(response, 500, e);
437
441
  return;
438
442
  }
439
443
  });
@@ -442,12 +446,12 @@ export default function createServer(
442
446
  try {
443
447
  const { action, data } = getActionData(request);
444
448
  if (['entity', 'entities', 'uid'].indexOf(action) === -1) {
445
- httpError(response, 400, 'Bad Request');
449
+ httpError(response, 400);
446
450
  return;
447
451
  }
448
452
  await doPutOrPatch(response, action, data, false);
449
453
  } catch (e: any) {
450
- httpError(response, 500, 'Internal Server Error', e);
454
+ httpError(response, 500, e);
451
455
  return;
452
456
  }
453
457
  });
@@ -456,12 +460,12 @@ export default function createServer(
456
460
  try {
457
461
  const { action, data } = getActionData(request);
458
462
  if (['entity', 'entities'].indexOf(action) === -1) {
459
- httpError(response, 400, 'Bad Request');
463
+ httpError(response, 400);
460
464
  return;
461
465
  }
462
466
  await doPutOrPatch(response, action, data, true);
463
467
  } catch (e: any) {
464
- httpError(response, 500, 'Internal Server Error', e);
468
+ httpError(response, 500, e);
465
469
  return;
466
470
  }
467
471
  });
@@ -474,7 +478,7 @@ export default function createServer(
474
478
  ) {
475
479
  if (action === 'uid') {
476
480
  if (typeof data.name !== 'string' || typeof data.value !== 'number') {
477
- httpError(response, 400, 'Bad Request');
481
+ httpError(response, 400);
478
482
  return;
479
483
  }
480
484
  if (response.locals.nymph.tilmeld) {
@@ -484,7 +488,7 @@ export default function createServer(
484
488
  TilmeldAccessLevels.FULL_ACCESS
485
489
  )
486
490
  ) {
487
- httpError(response, 403, 'Forbidden');
491
+ httpError(response, 403);
488
492
  return;
489
493
  }
490
494
  }
@@ -492,11 +496,11 @@ export default function createServer(
492
496
  try {
493
497
  result = await response.locals.nymph.setUID(data.name, data.value);
494
498
  } catch (e: any) {
495
- httpError(response, 500, 'Internal Server Error', e);
499
+ httpError(response, 500, e);
496
500
  return;
497
501
  }
498
502
  if (!result) {
499
- httpError(response, 500, 'Internal Server Error');
503
+ httpError(response, 500);
500
504
  return;
501
505
  }
502
506
  response.status(200);
@@ -558,13 +562,13 @@ export default function createServer(
558
562
  }
559
563
  if (!hadSuccess) {
560
564
  if (invalidRequest) {
561
- httpError(response, 400, 'Bad Request', lastException);
565
+ httpError(response, 400, lastException);
562
566
  } else if (conflict) {
563
- httpError(response, 409, 'Conflict');
567
+ httpError(response, 409);
564
568
  } else if (notfound) {
565
- httpError(response, 404, 'Not Found');
569
+ httpError(response, 404);
566
570
  } else {
567
- httpError(response, 500, 'Internal Server Error', lastException);
571
+ httpError(response, 500, lastException);
568
572
  }
569
573
  return;
570
574
  }
@@ -583,7 +587,7 @@ export default function createServer(
583
587
  const { action, data: dataConst } = getActionData(request);
584
588
  let data = dataConst;
585
589
  if (['entity', 'entities', 'uid'].indexOf(action) === -1) {
586
- httpError(response, 400, 'Bad Request');
590
+ httpError(response, 400);
587
591
  return;
588
592
  }
589
593
  if (['entity', 'entities'].indexOf(action) !== -1) {
@@ -592,7 +596,6 @@ export default function createServer(
592
596
  }
593
597
  const deleted = [];
594
598
  let failures = false;
595
- let hadSuccess = false;
596
599
  let invalidRequest = false;
597
600
  let notfound = false;
598
601
  let lastException = null;
@@ -628,7 +631,6 @@ export default function createServer(
628
631
  try {
629
632
  if (await entity.$delete()) {
630
633
  deleted.push(entData.guid);
631
- hadSuccess = true;
632
634
  } else {
633
635
  failures = true;
634
636
  }
@@ -639,11 +641,11 @@ export default function createServer(
639
641
  }
640
642
  if (deleted.length === 0) {
641
643
  if (invalidRequest || !failures) {
642
- httpError(response, 400, 'Bad Request', lastException);
644
+ httpError(response, 400, lastException);
643
645
  } else if (notfound) {
644
- httpError(response, 404, 'Not Found');
646
+ httpError(response, 404);
645
647
  } else {
646
- httpError(response, 500, 'Internal Server Error', lastException);
648
+ httpError(response, 500, lastException);
647
649
  }
648
650
  return;
649
651
  }
@@ -656,7 +658,7 @@ export default function createServer(
656
658
  }
657
659
  } else {
658
660
  if (typeof data !== 'string') {
659
- httpError(response, 400, 'Bad Request');
661
+ httpError(response, 400);
660
662
  return;
661
663
  }
662
664
  if (response.locals.nymph.tilmeld) {
@@ -666,7 +668,7 @@ export default function createServer(
666
668
  TilmeldAccessLevels.FULL_ACCESS
667
669
  )
668
670
  ) {
669
- httpError(response, 403, 'Forbidden');
671
+ httpError(response, 403);
670
672
  return;
671
673
  }
672
674
  }
@@ -674,11 +676,11 @@ export default function createServer(
674
676
  try {
675
677
  result = await response.locals.nymph.deleteUID(data);
676
678
  } catch (e: any) {
677
- httpError(response, 500, 'Internal Server Error', e);
679
+ httpError(response, 500, e);
678
680
  return;
679
681
  }
680
682
  if (!result) {
681
- httpError(response, 500, 'Internal Server Error');
683
+ httpError(response, 500);
682
684
  return;
683
685
  }
684
686
  response.status(200);
@@ -686,7 +688,7 @@ export default function createServer(
686
688
  response.send(JSON.stringify(result));
687
689
  }
688
690
  } catch (e: any) {
689
- httpError(response, 500, 'Internal Server Error', e);
691
+ httpError(response, 500, e);
690
692
  return;
691
693
  }
692
694
  });
@@ -763,23 +765,31 @@ export default function createServer(
763
765
  /**
764
766
  * Return the request with an HTTP error response.
765
767
  *
766
- * @param errorCode The HTTP status code.
767
- * @param message The message to place on the HTTP status header line.
768
- * @param error An optional exception object to report.
768
+ * @param res The server response object.
769
+ * @param defaultStatusCode The HTTP status code to use if none is given in the error object.
770
+ * @param error An optional error object to report.
769
771
  */
770
772
  function httpError(
771
773
  res: NymphResponse,
772
- errorCode: number,
773
- message: string,
774
- error?: Error
774
+ defaultStatusCode: number,
775
+ error?: Error & { status?: number; statusText?: string }
775
776
  ) {
777
+ const status = error?.status || defaultStatusCode;
778
+ const statusText =
779
+ error?.statusText ||
780
+ (error?.status == null
781
+ ? statusDescriptions[defaultStatusCode]
782
+ : error.status in statusDescriptions &&
783
+ statusDescriptions[error.status]) ||
784
+ 'Internal Server Error';
776
785
  if (!res.headersSent) {
777
- res.status(errorCode);
786
+ res.status(status);
778
787
  res.setHeader('Content-Type', 'application/json');
779
788
  }
780
789
  if (error) {
781
790
  res.send({
782
- textStatus: `${errorCode} ${message}`,
791
+ textStatus: `${status} ${statusText}`,
792
+ statusText,
783
793
  message: error.message,
784
794
  error,
785
795
  ...(process.env.NODE_ENV !== 'production'
@@ -787,9 +797,79 @@ export default function createServer(
787
797
  : {}),
788
798
  });
789
799
  } else {
790
- res.send({ textStatus: `${errorCode} ${message}` });
800
+ res.send({
801
+ textStatus: `${status} ${statusText}`,
802
+ statusText,
803
+ message: statusText,
804
+ });
791
805
  }
792
806
  }
793
807
 
794
808
  return rest;
795
809
  }
810
+
811
+ const statusDescriptions: { [k: number]: string } = {
812
+ 100: 'Continue',
813
+ 101: 'Switching Protocols',
814
+ 102: 'Processing',
815
+ 103: 'Early Hints',
816
+ 200: 'OK',
817
+ 201: 'Created',
818
+ 202: 'Accepted',
819
+ 203: 'Non-Authoritative Information',
820
+ 204: 'No Content',
821
+ 205: 'Reset Content',
822
+ 206: 'Partial Content',
823
+ 207: 'Multi-Status',
824
+ 208: 'Already Reported',
825
+ 226: 'IM Used',
826
+ 300: 'Multiple Choices',
827
+ 301: 'Moved Permanently',
828
+ 302: 'Found',
829
+ 303: 'See Other',
830
+ 304: 'Not Modified',
831
+ 305: 'Use Proxy',
832
+ 306: 'Switch Proxy',
833
+ 307: 'Temporary Redirect',
834
+ 308: 'Permanent Redirect',
835
+ 400: 'Bad Request',
836
+ 401: 'Unauthorized',
837
+ 402: 'Payment Required',
838
+ 403: 'Forbidden',
839
+ 404: 'Not Found',
840
+ 405: 'Method Not Allowed',
841
+ 406: 'Not Acceptable',
842
+ 407: 'Proxy Authentication Required',
843
+ 408: 'Request Timeout',
844
+ 409: 'Conflict',
845
+ 410: 'Gone',
846
+ 411: 'Length Required',
847
+ 412: 'Precondition Failed',
848
+ 413: 'Payload Too Large',
849
+ 414: 'URI Too Long',
850
+ 415: 'Unsupported Media Type',
851
+ 416: 'Range Not Satisfiable',
852
+ 417: 'Expectation Failed',
853
+ 418: "I'm a teapot",
854
+ 421: 'Misdirected Request',
855
+ 422: 'Unprocessable Entity',
856
+ 423: 'Locked',
857
+ 424: 'Failed Dependency',
858
+ 425: 'Too Early',
859
+ 426: 'Upgrade Required',
860
+ 428: 'Precondition Required',
861
+ 429: 'Too Many Requests',
862
+ 431: 'Request Header Fields Too Large',
863
+ 451: 'Unavailable For Legal Reasons',
864
+ 500: 'Internal Server Error',
865
+ 501: 'Not Implemented',
866
+ 502: 'Bad Gateway',
867
+ 503: 'Service Unavailable',
868
+ 504: 'Gateway Timeout',
869
+ 505: 'HTTP Version Not Supported',
870
+ 506: 'Variant Also Negotiates',
871
+ 507: 'Insufficient Storage',
872
+ 508: 'Loop Detected',
873
+ 510: 'Not Extended',
874
+ 511: 'Network Authentication Required',
875
+ };
@@ -1,6 +1,8 @@
1
1
  import { Entity as EntityServer, EntityInvalidDataError } from '@nymphjs/nymph';
2
2
  import { Entity } from '@nymphjs/client';
3
3
 
4
+ import { HttpError } from './HttpError';
5
+
4
6
  export type EmployeeBaseData<T> = {
5
7
  name?: string;
6
8
  id?: number;
@@ -34,6 +36,8 @@ export class EmployeeModel extends EntityServer<EmployeeModelData> {
34
36
  '$testMethodStateless',
35
37
  '$testMethod',
36
38
  '$throwError',
39
+ '$throwHttpError',
40
+ '$throwHttpErrorWithDescription',
37
41
  ];
38
42
  public static clientEnabledStaticMethods = ['testStatic', 'throwErrorStatic'];
39
43
  protected $protectedTags = ['employee'];
@@ -121,6 +125,14 @@ export class EmployeeModel extends EntityServer<EmployeeModelData> {
121
125
  throw new BadFunctionCallError('This function only throws errors.');
122
126
  }
123
127
 
128
+ public $throwHttpError() {
129
+ throw new HttpError('A 501 HTTP error.', 501);
130
+ }
131
+
132
+ public $throwHttpErrorWithDescription() {
133
+ throw new HttpError('A 512 HTTP error.', 512, 'Some Error');
134
+ }
135
+
124
136
  public static inaccessibleMethod() {
125
137
  return true;
126
138
  }
@@ -169,6 +181,14 @@ export class Employee extends Entity<EmployeeData> {
169
181
  return this.$serverCall('$throwError', []);
170
182
  }
171
183
 
184
+ $throwHttpError() {
185
+ return this.$serverCall('$throwHttpError', []);
186
+ }
187
+
188
+ $throwHttpErrorWithDescription() {
189
+ return this.$serverCall('$throwHttpErrorWithDescription', []);
190
+ }
191
+
172
192
  static testStatic(value: number) {
173
193
  return this.serverCallStatic('testStatic', [value]);
174
194
  }