dce-reactkit 3.2.0-beta.2 → 3.2.0-beta.4

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.
@@ -23,6 +23,7 @@ import LogSource from '../types/LogSource';
23
23
  import LogTypeSpecificInfo from '../types/Log/LogTypeSpecificInfo';
24
24
  import LogMainInfo from '../types/Log/LogMainInfo';
25
25
  import LogSourceSpecificInfo from '../types/Log/LogSourceSpecificInfo';
26
+ import LogBuiltInCategory from '../types/LogBuiltInCategory';
26
27
 
27
28
  /**
28
29
  * Generate an express API route handler
@@ -469,109 +470,166 @@ const genRouteHandler = (
469
470
  // that indicates that this is actually a client event, but we don't
470
471
  // include that in the LogFunction type because this is internal and
471
472
  // hidden from users
473
+ try {
474
+ // Parse user agent
475
+ const {
476
+ browser,
477
+ device,
478
+ } = parseUserAgent(req.headers['user-agent']);
479
+
480
+ // Get time info in ET
481
+ const {
482
+ timestamp,
483
+ year,
484
+ month,
485
+ day,
486
+ hour,
487
+ minute,
488
+ } = getTimeInfoInET();
489
+
490
+ // Main log info
491
+ const mainLogInfo: LogMainInfo = {
492
+ id: `${launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
493
+ userFirstName: launchInfo.userFirstName,
494
+ userLastName: launchInfo.userLastName,
495
+ userEmail: launchInfo.userEmail,
496
+ userId: launchInfo.userId,
497
+ isLearner: !!launchInfo.isLearner,
498
+ isAdmin: !!launchInfo.isAdmin,
499
+ isTTM: !!launchInfo.isTTM,
500
+ courseId: launchInfo.courseId,
501
+ courseName: launchInfo.courseName,
502
+ browser,
503
+ device,
504
+ year,
505
+ month,
506
+ day,
507
+ hour,
508
+ minute,
509
+ timestamp,
510
+ category: (
511
+ typeof opts.category === 'string'
512
+ ? opts.category
513
+ : opts.category.name
514
+ ),
515
+ subcategory: (
516
+ typeof opts.category === 'string'
517
+ ? opts.subcategory
518
+ : ((opts.subcategory as any) ?? {}).name
519
+ ),
520
+ tags: opts.tags ?? [],
521
+ metadata: opts.metadata ?? {},
522
+ };
523
+
524
+ // Type-specific info
525
+ const typeSpecificInfo: LogTypeSpecificInfo = (
526
+ ('error' in opts && opts.error)
527
+ ? {
528
+ type: LogType.Error,
529
+ errorMessage: opts.error.message ?? 'Unknown message',
530
+ errorCode: opts.error.code ?? ReactKitErrorCode.NoCode,
531
+ errorStack: opts.error.stack ?? 'No stack',
532
+ }
533
+ : {
534
+ type: LogType.Action,
535
+ target: (opts as any).target ?? 'Unknown',
536
+ action: (opts as any).action ?? 'Unknown'
537
+ }
538
+ );
472
539
 
473
- // Parse user agent
474
- const {
475
- browser,
476
- device,
477
- } = parseUserAgent(req.headers['user-agent']);
478
-
479
- // Get time info in ET
480
- const {
481
- timestamp,
482
- year,
483
- month,
484
- day,
485
- hour,
486
- minute,
487
- } = getTimeInfoInET();
488
-
489
- // Main log info
490
- const mainLogInfo: LogMainInfo = {
491
- id: `${launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
492
- userFirstName: launchInfo.userFirstName,
493
- userLastName: launchInfo.userLastName,
494
- userEmail: launchInfo.userEmail,
495
- userId: launchInfo.userId,
496
- isLearner: !!launchInfo.isLearner,
497
- isAdmin: !!launchInfo.isAdmin,
498
- isTTM: !!launchInfo.isTTM,
499
- courseId: launchInfo.courseId,
500
- courseName: launchInfo.courseName,
501
- browser,
502
- device,
503
- year,
504
- month,
505
- day,
506
- hour,
507
- minute,
508
- timestamp,
509
- category: (
510
- typeof opts.category === 'string'
511
- ? opts.category
512
- : opts.category.name
513
- ),
514
- subcategory: (
515
- typeof opts.category === 'string'
516
- ? opts.subcategory
517
- : ((opts.subcategory as any) ?? { name: 'none' }).name
518
- ),
519
- tags: opts.tags ?? [],
520
- metadata: opts.metadata ?? {},
521
- };
522
-
523
- // Type-specific info
524
- const typeSpecificInfo: LogTypeSpecificInfo = (
525
- ('error' in opts && opts.error)
526
- ? {
527
- type: LogType.Error,
528
- errorMessage: opts.error.message ?? 'Unknown message',
529
- errorCode: opts.error.code ?? ReactKitErrorCode.NoCode,
530
- errorStack: opts.error.stack ?? 'No stack',
531
- }
532
- : {
533
- type: LogType.Action,
534
- target: (opts as any).target ?? 'Unknown',
535
- action: (opts as any).action ?? 'Unknown'
536
- }
537
- );
538
-
539
- // Source-specific info
540
- const sourceSpecificInfo: LogSourceSpecificInfo = (
541
- (opts as any).overrideAsClientEvent
542
- ? {
543
- source: LogSource.Client,
544
- }
545
- : {
546
- source: LogSource.Server,
547
- routePath: req.path,
548
- routeTemplate: req.route.path,
549
- }
550
- );
540
+ // Source-specific info
541
+ const sourceSpecificInfo: LogSourceSpecificInfo = (
542
+ (opts as any).overrideAsClientEvent
543
+ ? {
544
+ source: LogSource.Client,
545
+ }
546
+ : {
547
+ source: LogSource.Server,
548
+ routePath: req.path,
549
+ routeTemplate: req.route.path,
550
+ }
551
+ );
551
552
 
552
- // Build log event
553
- const log: Log = {
554
- ...mainLogInfo,
555
- ...typeSpecificInfo,
556
- ...sourceSpecificInfo,
557
- };
558
-
559
- // Either print to console or save to db
560
- const logCollection = internalGetLogCollection();
561
- if (logCollection) {
562
- // Store to the log collection
563
- await logCollection.insert(log);
564
- } else {
565
- // Print to console
566
- if (log.type === LogType.Error) {
567
- console.error('dce-reactkit error log:', log);
553
+ // Build log event
554
+ const log: Log = {
555
+ ...mainLogInfo,
556
+ ...typeSpecificInfo,
557
+ ...sourceSpecificInfo,
558
+ };
559
+
560
+ // Either print to console or save to db
561
+ const logCollection = internalGetLogCollection();
562
+ if (logCollection) {
563
+ // Store to the log collection
564
+ await logCollection.insert(log);
568
565
  } else {
569
- console.log('dce-reactkit action log:', log);
566
+ // Print to console
567
+ if (log.type === LogType.Error) {
568
+ console.error('dce-reactkit error log:', log);
569
+ } else {
570
+ console.log('dce-reactkit action log:', log);
571
+ }
570
572
  }
571
- }
572
573
 
573
- // Return log entry
574
- return log;
574
+ // Return log entry
575
+ return log;
576
+ } catch (err) {
577
+ // Print because we cannot store the error
578
+ console.error('Could not log the following:', opts);
579
+
580
+ // Create a dummy log to return
581
+ const dummyMainInfo: LogMainInfo = {
582
+ id: '-1',
583
+ userFirstName: 'Unknown',
584
+ userLastName: 'Unknown',
585
+ userEmail: 'unknown@harvard.edu',
586
+ userId: 1,
587
+ isLearner: false,
588
+ isAdmin: false,
589
+ isTTM: false,
590
+ courseId: 1,
591
+ courseName: 'Unknown',
592
+ browser: {
593
+ name: 'Unknown',
594
+ version: 'Unknown',
595
+ },
596
+ device: {
597
+ isMobile: false,
598
+ os: 'Unknown',
599
+ },
600
+ year: 1,
601
+ month: 1,
602
+ day: 1,
603
+ hour: 1,
604
+ minute: 1,
605
+ timestamp: Date.now(),
606
+ tags: [],
607
+ metadata: {},
608
+ category: LogBuiltInCategory.Uncategorized,
609
+ subcategory: LogBuiltInCategory.Uncategorized,
610
+ };
611
+
612
+ const dummyTypeSpecificInfo: LogTypeSpecificInfo = {
613
+ type: LogType.Error,
614
+ errorMessage: 'Unknown',
615
+ errorCode: 'Unknown',
616
+ errorStack: 'No Stack',
617
+ };
618
+
619
+ const dummySourceSpecificInfo: LogSourceSpecificInfo = {
620
+ source: LogSource.Server,
621
+ routePath: req.path,
622
+ routeTemplate: req.route.path,
623
+ };
624
+
625
+ const log: Log = {
626
+ ...dummyMainInfo,
627
+ ...dummyTypeSpecificInfo,
628
+ ...dummySourceSpecificInfo,
629
+ };
630
+
631
+ return log;
632
+ }
575
633
  };
576
634
 
577
635
  /*------------------------------------------------------------------------*/
@@ -625,6 +683,22 @@ const genRouteHandler = (
625
683
  ) => {
626
684
  const html = genErrorPage(opts);
627
685
  send(html, opts.status ?? 500);
686
+
687
+ // Log
688
+ logServerEvent({
689
+ category: LogBuiltInCategory.ServerRenderedErrorPage,
690
+ error: {
691
+ message: `${opts.title}: ${opts.description}`,
692
+ code: opts.code,
693
+ },
694
+ metadata: {
695
+ title: opts.title,
696
+ description: opts.description,
697
+ code: opts.code,
698
+ pageTitle: opts.pageTitle,
699
+ status: opts.status ?? 500,
700
+ },
701
+ });
628
702
  };
629
703
 
630
704
  // Call the handler
@@ -649,7 +723,15 @@ const genRouteHandler = (
649
723
  } catch (err) {
650
724
  // Send error to client (only if next wasn't called)
651
725
  if (!responseSent) {
652
- return handleError(res, err);
726
+ handleError(res, err);
727
+
728
+ // Log server-side error
729
+ logServerEvent({
730
+ category: LogBuiltInCategory.ServerEndpointError,
731
+ error: err,
732
+ });
733
+
734
+ return;
653
735
  }
654
736
 
655
737
  // Log error that was not responded with
package/src/index.ts CHANGED
@@ -49,6 +49,7 @@ import stringsToHumanReadableList from './helpers/stringsToHumanReadableList';
49
49
  import onlyKeepLetters from './helpers/onlyKeepLetters';
50
50
  import parallelLimit from './helpers/parallelLimit';
51
51
  import logClientEvent from './helpers/logClientEvent';
52
+ import initLogCollection from './server/initLogCollection';
52
53
 
53
54
  // Import types
54
55
  import ModalButtonType from './types/ModalButtonType';
@@ -62,6 +63,7 @@ import Log from './types/Log';
62
63
  import LogType from './types/LogType';
63
64
  import LogSource from './types/LogSource';
64
65
  import LogAction from './types/LogAction';
66
+ import LogBuiltInCategory from './types/LogBuiltInCategory';
65
67
 
66
68
  // Component-specific-types
67
69
  import PickableItem from './components/ItemPicker/types/PickableItem';
@@ -122,6 +124,7 @@ export {
122
124
  genRouteHandler,
123
125
  handleError,
124
126
  handleSuccess,
127
+ initLogCollection,
125
128
  // Types
126
129
  ModalButtonType,
127
130
  ModalSize,
@@ -133,6 +136,7 @@ export {
133
136
  LogType,
134
137
  LogSource,
135
138
  LogAction,
139
+ LogBuiltInCategory,
136
140
  // Component-specific-types
137
141
  PickableItem,
138
142
  // Server types
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Initialize a log collection given the dce-mango Collection class
3
+ * @author Gabe Abrams
4
+ * @param Collection the Collection class from dce-mango
5
+ * @returns initialized logCollection
6
+ */
7
+ const initLogCollection = (Collection: any) => {
8
+ return new Collection(
9
+ 'Log',
10
+ {
11
+ uniqueIndexKey: 'id',
12
+ indexKeys: [
13
+ 'courseId',
14
+ 'category',
15
+ 'subcategory',
16
+ 'tags',
17
+ ],
18
+ },
19
+ );
20
+ };
21
+
22
+ export default initLogCollection;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Built-in categories for logs
3
+ * @author Gabe Abrams
4
+ */
5
+ enum LogBuiltInCategory {
6
+ Uncategorized = 'n/a',
7
+ ServerRenderedErrorPage = '_server-rendered-error-page',
8
+ ServerEndpointError = '_server-endpoint-error',
9
+ };
10
+
11
+ export default LogBuiltInCategory;