dce-reactkit 3.1.19 → 3.2.0-beta.1

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.
Files changed (54) hide show
  1. package/dist/cjs/index.js +373 -25
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/AppWrapper.d.ts +14 -3
  4. package/dist/cjs/types/constants/LOG_ROUTE_PATH.d.ts +6 -0
  5. package/dist/cjs/types/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
  6. package/dist/cjs/types/helpers/genRouteHandler.d.ts +2 -0
  7. package/dist/cjs/types/helpers/logClientEvent.d.ts +7 -0
  8. package/dist/cjs/types/helpers/parseUserAgent.d.ts +17 -0
  9. package/dist/cjs/types/index.d.ts +5 -1
  10. package/dist/cjs/types/server/initServer.d.ts +13 -0
  11. package/dist/cjs/types/types/Log/LogMainInfo.d.ts +37 -0
  12. package/dist/cjs/types/types/Log/LogSourceSpecificInfo.d.ts +13 -0
  13. package/dist/cjs/types/types/Log/LogTypeSpecificInfo.d.ts +17 -0
  14. package/dist/cjs/types/types/Log/index.d.ts +10 -0
  15. package/dist/cjs/types/types/LogAction.d.ts +22 -0
  16. package/dist/cjs/types/types/LogFunction.d.ts +20 -0
  17. package/dist/cjs/types/types/LogSource.d.ts +9 -0
  18. package/dist/cjs/types/types/LogType.d.ts +9 -0
  19. package/dist/esm/index.js +371 -26
  20. package/dist/esm/index.js.map +1 -1
  21. package/dist/esm/types/components/AppWrapper.d.ts +14 -3
  22. package/dist/esm/types/constants/LOG_ROUTE_PATH.d.ts +6 -0
  23. package/dist/esm/types/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
  24. package/dist/esm/types/helpers/genRouteHandler.d.ts +2 -0
  25. package/dist/esm/types/helpers/logClientEvent.d.ts +7 -0
  26. package/dist/esm/types/helpers/parseUserAgent.d.ts +17 -0
  27. package/dist/esm/types/index.d.ts +5 -1
  28. package/dist/esm/types/server/initServer.d.ts +13 -0
  29. package/dist/esm/types/types/Log/LogMainInfo.d.ts +37 -0
  30. package/dist/esm/types/types/Log/LogSourceSpecificInfo.d.ts +13 -0
  31. package/dist/esm/types/types/Log/LogTypeSpecificInfo.d.ts +17 -0
  32. package/dist/esm/types/types/Log/index.d.ts +10 -0
  33. package/dist/esm/types/types/LogAction.d.ts +22 -0
  34. package/dist/esm/types/types/LogFunction.d.ts +20 -0
  35. package/dist/esm/types/types/LogSource.d.ts +9 -0
  36. package/dist/esm/types/types/LogType.d.ts +9 -0
  37. package/dist/index.d.ts +165 -19
  38. package/package.json +1 -1
  39. package/src/components/AppWrapper.tsx +32 -13
  40. package/src/constants/LOG_ROUTE_PATH.ts +9 -0
  41. package/src/constants/ROUTE_PATH_PREFIX.ts +7 -0
  42. package/src/helpers/genRouteHandler.ts +129 -1
  43. package/src/helpers/logClientEvent.tsx +50 -0
  44. package/src/helpers/parseUserAgent.ts +108 -0
  45. package/src/index.ts +8 -0
  46. package/src/server/initServer.ts +90 -0
  47. package/src/types/Log/LogMainInfo.ts +64 -0
  48. package/src/types/Log/LogSourceSpecificInfo.ts +25 -0
  49. package/src/types/Log/LogTypeSpecificInfo.ts +33 -0
  50. package/src/types/Log/index.ts +17 -0
  51. package/src/types/LogAction.ts +38 -0
  52. package/src/types/LogFunction.ts +41 -0
  53. package/src/types/LogSource.ts +12 -0
  54. package/src/types/LogType.ts +12 -0
@@ -0,0 +1,20 @@
1
+ import Log from './Log';
2
+ import LogAction from './LogAction';
3
+ /**
4
+ * Type of a log action function
5
+ * @author Gabe Abrams
6
+ */
7
+ declare type LogFunction = (opts: ({
8
+ category: string;
9
+ subcategory?: string;
10
+ tags?: string[];
11
+ metadata?: {
12
+ [k: string]: any;
13
+ };
14
+ } & ({
15
+ error: any;
16
+ } | {
17
+ target: string;
18
+ action: LogAction;
19
+ }))) => Promise<Log>;
20
+ export default LogFunction;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Source of a log event
3
+ * @author Gabe Abrams
4
+ */
5
+ declare enum LogSource {
6
+ Client = "client",
7
+ Server = "server"
8
+ }
9
+ export default LogSource;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Type of a log event
3
+ * @author Gabe Abrams
4
+ */
5
+ declare enum LogType {
6
+ Action = "action",
7
+ Error = "error"
8
+ }
9
+ export default LogType;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,21 @@
1
1
  /// <reference types="react" />
2
2
  import React from 'react';
3
3
 
4
+ /**
5
+ * Bootstrap variants
6
+ * @author Gabe Abrams
7
+ */
8
+ declare enum Variant {
9
+ Primary = "primary",
10
+ Secondary = "secondary",
11
+ Success = "success",
12
+ Warning = "warning",
13
+ Info = "info",
14
+ Danger = "danger",
15
+ Light = "light",
16
+ Dark = "dark"
17
+ }
18
+
4
19
  /**
5
20
  * A wrapper for the entire React app that adds global functionality like
6
21
  * handling for fatal error messages, adds bootstrap support
@@ -43,11 +58,21 @@ declare const alert: (title: string, text: string) => Promise<undefined>;
43
58
  * @author Gabe Abrams
44
59
  * @param title the title text to display at the top of the alert
45
60
  * @param text the text to display in the alert
46
- * @param [confirmButtonText=Okay] the text of the confirm button
47
- * @param [cancelButtonText=Cancel] the text of the cancel button
61
+ * @param [opts={}] additional options for the confirmation dialog
62
+ * @param [opts.confirmButtonText=Okay] the text of the confirm button
63
+ * @param [opts.confirmButtonVariant=Variant.Dark] the variant of the confirm
64
+ * button
65
+ * @param [opts.cancelButtonText=Cancel] the text of the cancel button
66
+ * @param [opts.cancelButtonVariant=Variant.Secondary] the variant of the cancel
67
+ * button
48
68
  * @returns true if the user confirmed
49
69
  */
50
- declare const confirm: (title: string, text: string, confirmButtonText?: string, cancelButtonText?: string) => Promise<boolean>;
70
+ declare const confirm: (title: string, text: string, opts?: {
71
+ confirmButtonText?: string | undefined;
72
+ confirmButtonVariant?: Variant | undefined;
73
+ cancelButtonText?: string | undefined;
74
+ cancelButtonVariant?: Variant | undefined;
75
+ } | undefined) => Promise<boolean>;
51
76
  /**
52
77
  * Show a fatal error message
53
78
  * @author Gabe Abrams
@@ -75,21 +100,6 @@ declare type Props$c = {
75
100
  };
76
101
  declare const ErrorBox: React.FC<Props$c>;
77
102
 
78
- /**
79
- * Bootstrap variants
80
- * @author Gabe Abrams
81
- */
82
- declare enum Variant {
83
- Primary = "primary",
84
- Secondary = "secondary",
85
- Success = "success",
86
- Warning = "warning",
87
- Info = "info",
88
- Danger = "danger",
89
- Light = "light",
90
- Dark = "dark"
91
- }
92
-
93
103
  /**
94
104
  * Types of buttons in the modal
95
105
  * @author Gabe Abrams
@@ -505,6 +515,135 @@ declare enum ParamType {
505
515
  StringOptional = "string-optional"
506
516
  }
507
517
 
518
+ /**
519
+ * Main information in a log event
520
+ * @author Gabe Abrams
521
+ */
522
+ declare type LogMainInfo = {
523
+ id: string;
524
+ userFirstName: string;
525
+ userLastName: string;
526
+ userEmail: string;
527
+ userId: number;
528
+ isLearner: boolean;
529
+ isAdmin: boolean;
530
+ isTTM: boolean;
531
+ courseId: number;
532
+ courseName: string;
533
+ browser: {
534
+ name: string;
535
+ version: string;
536
+ };
537
+ device: {
538
+ os: string;
539
+ isMobile: boolean;
540
+ };
541
+ year: number;
542
+ month: number;
543
+ day: number;
544
+ hour: number;
545
+ minute: number;
546
+ timestamp: number;
547
+ category: string;
548
+ subcategory: string;
549
+ tags: string[];
550
+ metadata?: {
551
+ [k: string]: any;
552
+ };
553
+ };
554
+
555
+ /**
556
+ * Source of a log event
557
+ * @author Gabe Abrams
558
+ */
559
+ declare enum LogSource {
560
+ Client = "client",
561
+ Server = "server"
562
+ }
563
+
564
+ /**
565
+ * Log info that is specific to the type of source
566
+ * @author Gabe Abrams
567
+ */
568
+ declare type LogSourceSpecificInfo = ({
569
+ source: LogSource.Client;
570
+ } | {
571
+ source: LogSource.Server;
572
+ routePath: string;
573
+ routeTemplate: string;
574
+ });
575
+
576
+ /**
577
+ * Types of actions
578
+ * @author Gabe Abrams
579
+ */
580
+ declare enum LogAction {
581
+ Open = "open",
582
+ Close = "close",
583
+ Expand = "expand",
584
+ Collapse = "collapse",
585
+ View = "view",
586
+ Interrupt = "interrupt",
587
+ Create = "create",
588
+ Edit = "edit",
589
+ Delete = "delete",
590
+ Add = "add",
591
+ Remove = "remove",
592
+ Activate = "activate",
593
+ Deactivate = "deactivate",
594
+ Peek = "peek",
595
+ Unknown = "unknown"
596
+ }
597
+
598
+ /**
599
+ * Type of a log event
600
+ * @author Gabe Abrams
601
+ */
602
+ declare enum LogType {
603
+ Action = "action",
604
+ Error = "error"
605
+ }
606
+
607
+ /**
608
+ * Log info that is specific to the type of log
609
+ * @author Gabe Abrams
610
+ */
611
+ declare type LogTypeSpecificInfo = ({
612
+ type: LogType.Error;
613
+ errorMessage: string;
614
+ errorCode: string;
615
+ errorStack: string;
616
+ } | {
617
+ type: LogType.Action;
618
+ target: string;
619
+ action: LogAction;
620
+ });
621
+
622
+ /**
623
+ * A single log event corresponding to an action performed by a user or an
624
+ * error encountered by a user
625
+ * @author Gabe Abrams
626
+ */
627
+ declare type Log = (LogMainInfo & LogSourceSpecificInfo & LogTypeSpecificInfo);
628
+
629
+ /**
630
+ * Type of a log action function
631
+ * @author Gabe Abrams
632
+ */
633
+ declare type LogFunction = (opts: ({
634
+ category: string;
635
+ subcategory?: string;
636
+ tags?: string[];
637
+ metadata?: {
638
+ [k: string]: any;
639
+ };
640
+ } & ({
641
+ error: any;
642
+ } | {
643
+ target: string;
644
+ action: LogAction;
645
+ }))) => Promise<Log>;
646
+
508
647
  /**
509
648
  * Generate an express API route handler
510
649
  * @author Gabe Abrams
@@ -546,6 +685,7 @@ declare const genRouteHandler: (opts: {
546
685
  pageTitle?: string | undefined;
547
686
  status?: number | undefined;
548
687
  } | undefined) => void;
688
+ logServerEvent: LogFunction;
549
689
  }) => any;
550
690
  skipSessionCheck?: boolean | undefined;
551
691
  }) => (req: any, res: any, next: () => void) => Promise<undefined>;
@@ -584,10 +724,16 @@ declare type GetLaunchInfoFunction = (req: any) => {
584
724
  * Prepare dce-reactkit to run on the server
585
725
  * @author Gabe Abrams
586
726
  * @param opts object containing all arguments
727
+ * @param opts.app express app from inside of the postprocessor function that
728
+ * we will add routes to
587
729
  * @param opts.getLaunchInfo CACCL LTI's get launch info function
730
+ * @param [opts.logCollection] mongo collection from dce-mango to use for
731
+ * storing logs. If none is included, logs are written to the console
588
732
  */
589
733
  declare const initServer: (opts: {
734
+ app: any;
590
735
  getLaunchInfo: GetLaunchInfoFunction;
736
+ logCollection?: any;
591
737
  }) => void;
592
738
 
593
739
  /**
@@ -717,4 +863,4 @@ declare enum DayOfWeek {
717
863
  Sunday = "u"
718
864
  }
719
865
 
720
- export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, Variant, abbreviate, alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, getHumanReadableDate, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initServer, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
866
+ export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, Log, LogAction, LogSource, LogType, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, Variant, abbreviate, alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, getHumanReadableDate, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initServer, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dce-reactkit",
3
- "version": "3.1.19",
3
+ "version": "3.2.0-beta.1",
4
4
  "description": "Shared components for Harvard DCE apps",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -14,6 +14,7 @@ import ErrorBox from './ErrorBox';
14
14
  import ReactKitErrorCode from '../types/ReactKitErrorCode';
15
15
  import ModalButtonType from '../types/ModalButtonType';
16
16
  import ModalType from '../types/ModalType';
17
+ import Variant from '../types/Variant';
17
18
 
18
19
  // Import shared components
19
20
  import Modal from './Modal';
@@ -133,8 +134,12 @@ let setConfirmInfo: (
133
134
  | {
134
135
  title: string,
135
136
  text: string,
136
- confirmButtonText: string,
137
- cancelButtonText: string,
137
+ opts: {
138
+ confirmButtonText?: string,
139
+ confirmButtonVariant?: Variant,
140
+ cancelButtonText?: string,
141
+ cancelButtonVariant?: Variant,
142
+ },
138
143
  }
139
144
  ),
140
145
  ) => void;
@@ -146,15 +151,24 @@ let onConfirmClosed: (confirmed: boolean) => void;
146
151
  * @author Gabe Abrams
147
152
  * @param title the title text to display at the top of the alert
148
153
  * @param text the text to display in the alert
149
- * @param [confirmButtonText=Okay] the text of the confirm button
150
- * @param [cancelButtonText=Cancel] the text of the cancel button
154
+ * @param [opts={}] additional options for the confirmation dialog
155
+ * @param [opts.confirmButtonText=Okay] the text of the confirm button
156
+ * @param [opts.confirmButtonVariant=Variant.Dark] the variant of the confirm
157
+ * button
158
+ * @param [opts.cancelButtonText=Cancel] the text of the cancel button
159
+ * @param [opts.cancelButtonVariant=Variant.Secondary] the variant of the cancel
160
+ * button
151
161
  * @returns true if the user confirmed
152
162
  */
153
163
  export const confirm = async (
154
164
  title: string,
155
165
  text: string,
156
- confirmButtonText: string = 'Okay',
157
- cancelButtonText: string = 'Cancel',
166
+ opts?: {
167
+ confirmButtonText?: string,
168
+ confirmButtonVariant?: Variant,
169
+ cancelButtonText?: string,
170
+ cancelButtonVariant?: Variant,
171
+ },
158
172
  ): Promise<boolean> => {
159
173
  // Fallback if confirm is not available
160
174
  if (!setConfirmInfo) {
@@ -172,8 +186,7 @@ export const confirm = async (
172
186
  setConfirmInfo({
173
187
  title,
174
188
  text,
175
- confirmButtonText,
176
- cancelButtonText,
189
+ opts: (opts ?? {}),
177
190
  });
178
191
  });
179
192
  };
@@ -290,7 +303,7 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
290
303
  undefined
291
304
  | {
292
305
  title: string,
293
- text: string
306
+ text: string,
294
307
  }
295
308
  >(undefined);
296
309
  setAlertInfo = setAlertInfoInner;
@@ -304,8 +317,12 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
304
317
  | {
305
318
  title: string,
306
319
  text: string,
307
- confirmButtonText: string,
308
- cancelButtonText: string,
320
+ opts: {
321
+ confirmButtonText?: string,
322
+ confirmButtonVariant?: Variant,
323
+ cancelButtonText?: string,
324
+ cancelButtonVariant?: Variant,
325
+ },
309
326
  }
310
327
  >(undefined);
311
328
  setConfirmInfo = setConfirmInfoInner;
@@ -358,8 +375,10 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
358
375
  key={`confirm-${confirmInfo.title}-${confirmInfo.text}`}
359
376
  title={confirmInfo.title}
360
377
  type={ModalType.OkayCancel}
361
- okayLabel={confirmInfo.confirmButtonText}
362
- cancelLabel={confirmInfo.cancelButtonText}
378
+ okayLabel={confirmInfo.opts.confirmButtonText}
379
+ okayVariant={confirmInfo.opts.confirmButtonVariant}
380
+ cancelLabel={confirmInfo.opts.cancelButtonText}
381
+ cancelVariant={confirmInfo.opts.cancelButtonVariant}
363
382
  onClose={(buttonType) => {
364
383
  setConfirmInfo(undefined);
365
384
  if (onConfirmClosed) {
@@ -0,0 +1,9 @@
1
+ import ROUTE_PATH_PREFIX from './ROUTE_PATH_PREFIX';
2
+
3
+ /**
4
+ * Path of the route for storing client-side logs
5
+ * @author Gabe Abrams
6
+ */
7
+ const LOG_ROUTE_PATH = `${ROUTE_PATH_PREFIX}/log`;
8
+
9
+ export default LOG_ROUTE_PATH;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Path that all routes start with
3
+ * @author Gabe Abrams
4
+ */
5
+ const ROUTE_PATH_PREFIX = '/dce-reactkit';
6
+
7
+ export default ROUTE_PATH_PREFIX;
@@ -1,5 +1,8 @@
1
1
  // Import caccl functions
2
- import { cacclGetLaunchInfo } from '../server/initServer';
2
+ import {
3
+ cacclGetLaunchInfo,
4
+ internalGetLogCollection,
5
+ } from '../server/initServer';
3
6
 
4
7
  // Import shared types
5
8
  import ReactKitErrorCode from '../types/ReactKitErrorCode';
@@ -9,6 +12,17 @@ import ParamType from '../types/ParamType';
9
12
  import handleError from './handleError';
10
13
  import handleSuccess from './handleSuccess';
11
14
  import genErrorPage from '../html/genErrorPage';
15
+ import parseUserAgent from './parseUserAgent';
16
+ import getTimeInfoInET from './getTimeInfoInET';
17
+
18
+ // Import shared types
19
+ import LogFunction from '../types/LogFunction';
20
+ import Log from '../types/Log';
21
+ import LogType from '../types/LogType';
22
+ import LogSource from '../types/LogSource';
23
+ import LogTypeSpecificInfo from '../types/Log/LogTypeSpecificInfo';
24
+ import LogMainInfo from '../types/Log/LogMainInfo';
25
+ import LogSourceSpecificInfo from '../types/Log/LogSourceSpecificInfo';
12
26
 
13
27
  /**
14
28
  * Generate an express API route handler
@@ -55,6 +69,7 @@ const genRouteHandler = (
55
69
  status?: number,
56
70
  },
57
71
  ) => void,
72
+ logServerEvent: LogFunction,
58
73
  },
59
74
  ) => any,
60
75
  skipSessionCheck?: boolean,
@@ -439,6 +454,118 @@ const genRouteHandler = (
439
454
  );
440
455
  }
441
456
 
457
+ /*----------------------------------------*/
458
+ /* Log Handler */
459
+ /*----------------------------------------*/
460
+
461
+ // Create a log handler function
462
+
463
+ /**
464
+ * Log an event on the server
465
+ * @author Gabe Abrams
466
+ */
467
+ const logServerEvent: LogFunction = async (opts) => {
468
+ // NOTE: internally, we slip through an opts.overrideAsClientEvent boolean
469
+ // that indicates that this is actually a client event, but we don't
470
+ // include that in the LogFunction type because this is internal and
471
+ // hidden from users
472
+
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: opts.category,
510
+ subcategory: opts.subcategory ?? 'none',
511
+ tags: opts.tags ?? [],
512
+ metadata: opts.metadata ?? {},
513
+ };
514
+
515
+ // Type-specific info
516
+ const typeSpecificInfo: LogTypeSpecificInfo = (
517
+ ('error' in opts && opts.error)
518
+ ? {
519
+ type: LogType.Error,
520
+ errorMessage: opts.error.message ?? 'Unknown message',
521
+ errorCode: opts.error.code ?? ReactKitErrorCode.NoCode,
522
+ errorStack: opts.error.stack ?? 'No stack',
523
+ }
524
+ : {
525
+ type: LogType.Action,
526
+ target: (opts as any).target ?? 'Unknown',
527
+ action: (opts as any).action ?? 'Unknown'
528
+ }
529
+ );
530
+
531
+ // Source-specific info
532
+ const sourceSpecificInfo: LogSourceSpecificInfo = (
533
+ (opts as any).overrideAsClientEvent
534
+ ? {
535
+ source: LogSource.Client,
536
+ }
537
+ : {
538
+ source: LogSource.Server,
539
+ routePath: req.path,
540
+ routeTemplate: req.route.path,
541
+ }
542
+ );
543
+
544
+ // Build log event
545
+ const log: Log = {
546
+ ...mainLogInfo,
547
+ ...typeSpecificInfo,
548
+ ...sourceSpecificInfo,
549
+ };
550
+
551
+ // Either print to console or save to db
552
+ const logCollection = internalGetLogCollection();
553
+ if (logCollection) {
554
+ // Store to the log collection
555
+ await logCollection.insert(log);
556
+ } else {
557
+ // Print to console
558
+ if (log.type === LogType.Error) {
559
+ console.error('dce-reactkit error log:', log);
560
+ } else {
561
+ console.log('dce-reactkit action log:', log);
562
+ }
563
+ }
564
+
565
+ // Return log entry
566
+ return log;
567
+ };
568
+
442
569
  /*------------------------------------------------------------------------*/
443
570
  /* Call handler */
444
571
  /*------------------------------------------------------------------------*/
@@ -504,6 +631,7 @@ const genRouteHandler = (
504
631
  },
505
632
  redirect,
506
633
  renderErrorPage,
634
+ logServerEvent,
507
635
  });
508
636
 
509
637
  // Send results to client (only if next wasn't called)
@@ -0,0 +1,50 @@
1
+ // Import shared types
2
+ import LOG_ROUTE_PATH from '../constants/LOG_ROUTE_PATH';
3
+ import LogFunction from '../types/LogFunction';
4
+
5
+ // Import shared functions
6
+ import visitServerEndpoint from './visitServerEndpoint';
7
+
8
+ /**
9
+ * Log a user action on the client (cannot be used on the server)
10
+ * @author Gabe Abrams
11
+ */
12
+ const logClientEvent: LogFunction = async (opts) => {
13
+ return visitServerEndpoint({
14
+ path: LOG_ROUTE_PATH,
15
+ method: 'POST',
16
+ params: {
17
+ category: opts.category,
18
+ subcategory: opts.subcategory,
19
+ tags: JSON.stringify(opts.tags),
20
+ metadata: JSON.stringify(opts.metadata ?? {}),
21
+ errorMessage: (
22
+ (opts as any).error
23
+ ? (opts as any).error.message
24
+ : undefined
25
+ ),
26
+ errorCode: (
27
+ (opts as any).error
28
+ ? (opts as any).error.code
29
+ : undefined
30
+ ),
31
+ errorStack: (
32
+ (opts as any).error
33
+ ? (opts as any).error.stack
34
+ : undefined
35
+ ),
36
+ target: (
37
+ (opts as any).target
38
+ ? (opts as any).target
39
+ : undefined
40
+ ),
41
+ action: (
42
+ (opts as any).action
43
+ ? (opts as any).action
44
+ : undefined
45
+ ),
46
+ },
47
+ });
48
+ };
49
+
50
+ export default logClientEvent;