dce-reactkit 3.1.20 → 3.2.0-beta.10
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/.vscode/settings.json +6 -0
- package/dist/cjs/index.js +517 -21
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/constants/LOG_ROUTE_PATH.d.ts +6 -0
- package/dist/cjs/types/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
- package/dist/cjs/types/helpers/genRouteHandler.d.ts +2 -0
- package/dist/cjs/types/helpers/logClientEvent.d.ts +7 -0
- package/dist/cjs/types/helpers/parseUserAgent.d.ts +17 -0
- package/dist/cjs/types/index.d.ts +8 -1
- package/dist/cjs/types/server/initLogCollection.d.ts +8 -0
- package/dist/cjs/types/server/initServer.d.ts +13 -0
- package/dist/cjs/types/types/Log/LogMainInfo.d.ts +37 -0
- package/dist/cjs/types/types/Log/LogSourceSpecificInfo.d.ts +13 -0
- package/dist/cjs/types/types/Log/LogTypeSpecificInfo.d.ts +17 -0
- package/dist/cjs/types/types/Log/index.d.ts +10 -0
- package/dist/cjs/types/types/LogAction.d.ts +23 -0
- package/dist/cjs/types/types/LogBuiltInMetadata.d.ts +15 -0
- package/dist/cjs/types/types/LogFunction.d.ts +24 -0
- package/dist/cjs/types/types/LogSource.d.ts +9 -0
- package/dist/cjs/types/types/LogType.d.ts +9 -0
- package/dist/esm/index.js +512 -22
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/constants/LOG_ROUTE_PATH.d.ts +6 -0
- package/dist/esm/types/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
- package/dist/esm/types/helpers/genRouteHandler.d.ts +2 -0
- package/dist/esm/types/helpers/logClientEvent.d.ts +7 -0
- package/dist/esm/types/helpers/parseUserAgent.d.ts +17 -0
- package/dist/esm/types/index.d.ts +8 -1
- package/dist/esm/types/server/initLogCollection.d.ts +8 -0
- package/dist/esm/types/server/initServer.d.ts +13 -0
- package/dist/esm/types/types/Log/LogMainInfo.d.ts +37 -0
- package/dist/esm/types/types/Log/LogSourceSpecificInfo.d.ts +13 -0
- package/dist/esm/types/types/Log/LogTypeSpecificInfo.d.ts +17 -0
- package/dist/esm/types/types/Log/index.d.ts +10 -0
- package/dist/esm/types/types/LogAction.d.ts +23 -0
- package/dist/esm/types/types/LogBuiltInMetadata.d.ts +15 -0
- package/dist/esm/types/types/LogFunction.d.ts +24 -0
- package/dist/esm/types/types/LogSource.d.ts +9 -0
- package/dist/esm/types/types/LogType.d.ts +9 -0
- package/dist/index.d.ts +171 -1
- package/package.json +1 -1
- package/src/components/AppWrapper.tsx +1 -1
- package/src/constants/LOG_ROUTE_PATH.ts +9 -0
- package/src/constants/ROUTE_PATH_PREFIX.ts +7 -0
- package/src/helpers/genRouteHandler.ts +233 -2
- package/src/helpers/logClientEvent.tsx +68 -0
- package/src/helpers/parseUserAgent.ts +108 -0
- package/src/index.ts +14 -0
- package/src/server/initLogCollection.ts +22 -0
- package/src/server/initServer.ts +90 -0
- package/src/types/Log/LogMainInfo.ts +64 -0
- package/src/types/Log/LogSourceSpecificInfo.ts +25 -0
- package/src/types/Log/LogTypeSpecificInfo.ts +33 -0
- package/src/types/Log/index.ts +17 -0
- package/src/types/LogAction.ts +40 -0
- package/src/types/LogBuiltInMetadata.ts +18 -0
- package/src/types/LogFunction.ts +43 -0
- package/src/types/LogSource.ts +12 -0
- package/src/types/LogType.ts +12 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in metadata for logs
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
declare const LogBuiltInMetadata: {
|
|
6
|
+
Context: {
|
|
7
|
+
Uncategorized: string;
|
|
8
|
+
ServerRenderedErrorPage: string;
|
|
9
|
+
ServerEndpointError: string;
|
|
10
|
+
};
|
|
11
|
+
Target: {
|
|
12
|
+
NoSpecificTarget: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export default LogBuiltInMetadata;
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
context: string | {
|
|
9
|
+
_: string;
|
|
10
|
+
};
|
|
11
|
+
subcontext?: string | {
|
|
12
|
+
_: string;
|
|
13
|
+
};
|
|
14
|
+
tags?: string[];
|
|
15
|
+
metadata?: {
|
|
16
|
+
[k: string]: any;
|
|
17
|
+
};
|
|
18
|
+
} & ({
|
|
19
|
+
error: any;
|
|
20
|
+
} | {
|
|
21
|
+
action: LogAction;
|
|
22
|
+
target?: string;
|
|
23
|
+
}))) => Promise<Log>;
|
|
24
|
+
export default LogFunction;
|
package/dist/esm/index.js
CHANGED
|
@@ -2116,9 +2116,42 @@ const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function
|
|
|
2116
2116
|
return body;
|
|
2117
2117
|
});
|
|
2118
2118
|
|
|
2119
|
+
/**
|
|
2120
|
+
* Path that all routes start with
|
|
2121
|
+
* @author Gabe Abrams
|
|
2122
|
+
*/
|
|
2123
|
+
const ROUTE_PATH_PREFIX = '/dce-reactkit';
|
|
2124
|
+
|
|
2125
|
+
/**
|
|
2126
|
+
* Path of the route for storing client-side logs
|
|
2127
|
+
* @author Gabe Abrams
|
|
2128
|
+
*/
|
|
2129
|
+
const LOG_ROUTE_PATH = `${ROUTE_PATH_PREFIX}/log`;
|
|
2130
|
+
|
|
2131
|
+
/**
|
|
2132
|
+
* Server-side API param types
|
|
2133
|
+
* @author Gabe Abrams
|
|
2134
|
+
*/
|
|
2135
|
+
var ParamType;
|
|
2136
|
+
(function (ParamType) {
|
|
2137
|
+
ParamType["Boolean"] = "boolean";
|
|
2138
|
+
ParamType["BooleanOptional"] = "boolean-optional";
|
|
2139
|
+
ParamType["Float"] = "float";
|
|
2140
|
+
ParamType["FloatOptional"] = "float-optional";
|
|
2141
|
+
ParamType["Int"] = "int";
|
|
2142
|
+
ParamType["IntOptional"] = "int-optional";
|
|
2143
|
+
ParamType["JSON"] = "json";
|
|
2144
|
+
ParamType["JSONOptional"] = "json-optional";
|
|
2145
|
+
ParamType["String"] = "string";
|
|
2146
|
+
ParamType["StringOptional"] = "string-optional";
|
|
2147
|
+
})(ParamType || (ParamType = {}));
|
|
2148
|
+
var ParamType$1 = ParamType;
|
|
2149
|
+
|
|
2119
2150
|
// Import custom error
|
|
2120
2151
|
// Stored copy of caccl functions
|
|
2121
2152
|
let _cacclGetLaunchInfo;
|
|
2153
|
+
// Stored copy of dce-mango log collection
|
|
2154
|
+
let _logCollection;
|
|
2122
2155
|
/*------------------------------------------------------------------------*/
|
|
2123
2156
|
/* Helpers */
|
|
2124
2157
|
/*------------------------------------------------------------------------*/
|
|
@@ -2134,6 +2167,15 @@ const cacclGetLaunchInfo = (req) => {
|
|
|
2134
2167
|
}
|
|
2135
2168
|
return _cacclGetLaunchInfo(req);
|
|
2136
2169
|
};
|
|
2170
|
+
/**
|
|
2171
|
+
* Get log collection
|
|
2172
|
+
* @author Gabe Abrams
|
|
2173
|
+
* @returns log collection if one was included during launch or null if we don't
|
|
2174
|
+
* have a log collection (yet)
|
|
2175
|
+
*/
|
|
2176
|
+
const internalGetLogCollection = () => {
|
|
2177
|
+
return _logCollection !== null && _logCollection !== void 0 ? _logCollection : null;
|
|
2178
|
+
};
|
|
2137
2179
|
/*------------------------------------------------------------------------*/
|
|
2138
2180
|
/* Main */
|
|
2139
2181
|
/*------------------------------------------------------------------------*/
|
|
@@ -2141,31 +2183,73 @@ const cacclGetLaunchInfo = (req) => {
|
|
|
2141
2183
|
* Prepare dce-reactkit to run on the server
|
|
2142
2184
|
* @author Gabe Abrams
|
|
2143
2185
|
* @param opts object containing all arguments
|
|
2186
|
+
* @param opts.app express app from inside of the postprocessor function that
|
|
2187
|
+
* we will add routes to
|
|
2144
2188
|
* @param opts.getLaunchInfo CACCL LTI's get launch info function
|
|
2189
|
+
* @param [opts.logCollection] mongo collection from dce-mango to use for
|
|
2190
|
+
* storing logs. If none is included, logs are written to the console
|
|
2145
2191
|
*/
|
|
2146
2192
|
const initServer = (opts) => {
|
|
2147
2193
|
_cacclGetLaunchInfo = opts.getLaunchInfo;
|
|
2194
|
+
_logCollection = opts.logCollection;
|
|
2195
|
+
/**
|
|
2196
|
+
* Log an event
|
|
2197
|
+
* @author Gabe Abrams
|
|
2198
|
+
* @param {string} context Context of the event (each app determines how to
|
|
2199
|
+
* organize its contexts)
|
|
2200
|
+
* @param {string} subcontext Subcontext of the event (each app determines
|
|
2201
|
+
* how to organize its subcontexts)
|
|
2202
|
+
* @param {string} tags stringified list of tags that apply to this action
|
|
2203
|
+
* (each app determines tag usage)
|
|
2204
|
+
* @param {string} metadata stringified object containing optional custom metadata
|
|
2205
|
+
* @param {string} [errorMessage] error message if type is an error
|
|
2206
|
+
* @param {string} [errorCode] error code if type is an error
|
|
2207
|
+
* @param {string} [errorStack] error stack if type is an error
|
|
2208
|
+
* @param {string} [target] Target of the action (each app determines the list
|
|
2209
|
+
* of targets) These are usually buttons, panels, elements, etc.
|
|
2210
|
+
* @param {LogAction} [action] the type of action performed on the target
|
|
2211
|
+
* @returns {Log}
|
|
2212
|
+
*/
|
|
2213
|
+
opts.app.post(LOG_ROUTE_PATH, genRouteHandler({
|
|
2214
|
+
paramTypes: {
|
|
2215
|
+
context: ParamType$1.String,
|
|
2216
|
+
subcontext: ParamType$1.String,
|
|
2217
|
+
tags: ParamType$1.JSON,
|
|
2218
|
+
metadata: ParamType$1.JSON,
|
|
2219
|
+
errorMessage: ParamType$1.StringOptional,
|
|
2220
|
+
errorCode: ParamType$1.StringOptional,
|
|
2221
|
+
errorStack: ParamType$1.StringOptional,
|
|
2222
|
+
target: ParamType$1.StringOptional,
|
|
2223
|
+
action: ParamType$1.StringOptional,
|
|
2224
|
+
},
|
|
2225
|
+
handler: ({ params, logServerEvent }) => {
|
|
2226
|
+
const log = logServerEvent((params.errorMessage || params.errorCode || params.errorStack)
|
|
2227
|
+
// Error
|
|
2228
|
+
? {
|
|
2229
|
+
context: params.context,
|
|
2230
|
+
subcontext: params.subcontext,
|
|
2231
|
+
tags: params.tags,
|
|
2232
|
+
metadata: params.metadata,
|
|
2233
|
+
error: {
|
|
2234
|
+
message: params.errorMessage,
|
|
2235
|
+
code: params.errorCode,
|
|
2236
|
+
stack: params.errorStack,
|
|
2237
|
+
},
|
|
2238
|
+
}
|
|
2239
|
+
// Action
|
|
2240
|
+
: {
|
|
2241
|
+
context: params.context,
|
|
2242
|
+
subcontext: params.subcontext,
|
|
2243
|
+
tags: params.tags,
|
|
2244
|
+
metadata: params.metadata,
|
|
2245
|
+
target: params.target,
|
|
2246
|
+
action: params.action,
|
|
2247
|
+
});
|
|
2248
|
+
return log;
|
|
2249
|
+
},
|
|
2250
|
+
}));
|
|
2148
2251
|
};
|
|
2149
2252
|
|
|
2150
|
-
/**
|
|
2151
|
-
* Server-side API param types
|
|
2152
|
-
* @author Gabe Abrams
|
|
2153
|
-
*/
|
|
2154
|
-
var ParamType;
|
|
2155
|
-
(function (ParamType) {
|
|
2156
|
-
ParamType["Boolean"] = "boolean";
|
|
2157
|
-
ParamType["BooleanOptional"] = "boolean-optional";
|
|
2158
|
-
ParamType["Float"] = "float";
|
|
2159
|
-
ParamType["FloatOptional"] = "float-optional";
|
|
2160
|
-
ParamType["Int"] = "int";
|
|
2161
|
-
ParamType["IntOptional"] = "int-optional";
|
|
2162
|
-
ParamType["JSON"] = "json";
|
|
2163
|
-
ParamType["JSONOptional"] = "json-optional";
|
|
2164
|
-
ParamType["String"] = "string";
|
|
2165
|
-
ParamType["StringOptional"] = "string-optional";
|
|
2166
|
-
})(ParamType || (ParamType = {}));
|
|
2167
|
-
var ParamType$1 = ParamType;
|
|
2168
|
-
|
|
2169
2253
|
// Import shared types
|
|
2170
2254
|
/**
|
|
2171
2255
|
* Handle an error and respond to the client
|
|
@@ -2358,6 +2442,195 @@ const genErrorPage = (opts = {}) => {
|
|
|
2358
2442
|
`;
|
|
2359
2443
|
};
|
|
2360
2444
|
|
|
2445
|
+
/**
|
|
2446
|
+
* Perform a rudimentary parsing of the user's browser agent string
|
|
2447
|
+
* @author Gabe Abrams
|
|
2448
|
+
* @param userAgent the user's browser agent
|
|
2449
|
+
* @returns user info
|
|
2450
|
+
*/
|
|
2451
|
+
const parseUserAgent = (userAgent) => {
|
|
2452
|
+
/* ------------- Browser ------------ */
|
|
2453
|
+
let browser = {
|
|
2454
|
+
name: 'Unknown',
|
|
2455
|
+
version: 'Unknown',
|
|
2456
|
+
};
|
|
2457
|
+
// Parse user agent
|
|
2458
|
+
let verOffset;
|
|
2459
|
+
let nameOffset;
|
|
2460
|
+
if ((verOffset = userAgent.indexOf('Opera')) !== -1) {
|
|
2461
|
+
// In Opera, the true version is after 'Opera' or after 'Version'
|
|
2462
|
+
browser = {
|
|
2463
|
+
name: 'Opera',
|
|
2464
|
+
version: userAgent.substring(verOffset + 6),
|
|
2465
|
+
};
|
|
2466
|
+
if ((verOffset = userAgent.indexOf('Version')) !== -1) {
|
|
2467
|
+
browser.version = userAgent.substring(verOffset + 8);
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
else if ((verOffset = userAgent.indexOf('MSIE')) !== -1) {
|
|
2471
|
+
// In MSIE, the true version is after 'MSIE' in userAgent
|
|
2472
|
+
browser = {
|
|
2473
|
+
name: 'Internet Explorer',
|
|
2474
|
+
version: userAgent.substring(verOffset + 5),
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2477
|
+
else if ((verOffset = userAgent.indexOf('Chrome')) !== -1) {
|
|
2478
|
+
// In Chrome, the true version is after 'Chrome'
|
|
2479
|
+
browser = {
|
|
2480
|
+
name: 'Chrome',
|
|
2481
|
+
version: userAgent.substring(verOffset + 7),
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
else if ((verOffset = userAgent.indexOf('Safari')) !== -1) {
|
|
2485
|
+
// In Safari, the true version is after 'Safari' or after 'Version'
|
|
2486
|
+
browser = {
|
|
2487
|
+
name: 'Safari',
|
|
2488
|
+
version: userAgent.substring(verOffset + 7),
|
|
2489
|
+
};
|
|
2490
|
+
if ((verOffset = userAgent.indexOf('Version')) !== -1) {
|
|
2491
|
+
browser.version = userAgent.substring(verOffset + 8);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
else if ((verOffset = userAgent.indexOf('Firefox')) != -1) {
|
|
2495
|
+
// In Firefox, the true version is after 'Firefox'
|
|
2496
|
+
browser = {
|
|
2497
|
+
name: 'Firefox',
|
|
2498
|
+
version: userAgent.substring(verOffset + 8),
|
|
2499
|
+
};
|
|
2500
|
+
}
|
|
2501
|
+
else if ((nameOffset = userAgent.lastIndexOf(' ') + 1)
|
|
2502
|
+
< (verOffset = userAgent.lastIndexOf('/'))) {
|
|
2503
|
+
browser = {
|
|
2504
|
+
name: userAgent.substring(nameOffset, verOffset),
|
|
2505
|
+
version: userAgent.substring(verOffset + 1),
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
// Postprocess version
|
|
2509
|
+
// trim the fullVersion string at semicolon/space if present
|
|
2510
|
+
let ix;
|
|
2511
|
+
if ((ix = browser.version.indexOf(';')) !== -1) {
|
|
2512
|
+
browser.version = browser.version.substring(0, ix);
|
|
2513
|
+
}
|
|
2514
|
+
if ((ix = browser.version.indexOf(' ')) !== -1) {
|
|
2515
|
+
browser.version = browser.version.substring(0, ix);
|
|
2516
|
+
}
|
|
2517
|
+
/* ------------- Device ------------- */
|
|
2518
|
+
// Detect os
|
|
2519
|
+
let os = 'Unknown';
|
|
2520
|
+
if (userAgent.includes('Linux')) {
|
|
2521
|
+
os = 'Linux';
|
|
2522
|
+
}
|
|
2523
|
+
else if (userAgent.includes('like Mac')) {
|
|
2524
|
+
os = 'iOS';
|
|
2525
|
+
}
|
|
2526
|
+
else if (userAgent.includes('Mac')) {
|
|
2527
|
+
os = 'Mac';
|
|
2528
|
+
}
|
|
2529
|
+
else if (userAgent.includes('Android')) {
|
|
2530
|
+
os = 'Android';
|
|
2531
|
+
}
|
|
2532
|
+
else if (userAgent.includes('Win')) {
|
|
2533
|
+
os = 'Win';
|
|
2534
|
+
}
|
|
2535
|
+
// Check if mobile
|
|
2536
|
+
const isMobile = !!userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry)/);
|
|
2537
|
+
// Device
|
|
2538
|
+
const device = {
|
|
2539
|
+
isMobile,
|
|
2540
|
+
os,
|
|
2541
|
+
};
|
|
2542
|
+
/* ------------- Finish ------------- */
|
|
2543
|
+
// Return info
|
|
2544
|
+
return {
|
|
2545
|
+
browser,
|
|
2546
|
+
device,
|
|
2547
|
+
};
|
|
2548
|
+
};
|
|
2549
|
+
|
|
2550
|
+
/**
|
|
2551
|
+
* Type of a log event
|
|
2552
|
+
* @author Gabe Abrams
|
|
2553
|
+
*/
|
|
2554
|
+
var LogType;
|
|
2555
|
+
(function (LogType) {
|
|
2556
|
+
// User action
|
|
2557
|
+
LogType["Action"] = "action";
|
|
2558
|
+
// Error
|
|
2559
|
+
LogType["Error"] = "error";
|
|
2560
|
+
})(LogType || (LogType = {}));
|
|
2561
|
+
var LogType$1 = LogType;
|
|
2562
|
+
|
|
2563
|
+
/**
|
|
2564
|
+
* Source of a log event
|
|
2565
|
+
* @author Gabe Abrams
|
|
2566
|
+
*/
|
|
2567
|
+
var LogSource;
|
|
2568
|
+
(function (LogSource) {
|
|
2569
|
+
// Client
|
|
2570
|
+
LogSource["Client"] = "client";
|
|
2571
|
+
// Server
|
|
2572
|
+
LogSource["Server"] = "server";
|
|
2573
|
+
})(LogSource || (LogSource = {}));
|
|
2574
|
+
var LogSource$1 = LogSource;
|
|
2575
|
+
|
|
2576
|
+
/**
|
|
2577
|
+
* Built-in metadata for logs
|
|
2578
|
+
* @author Gabe Abrams
|
|
2579
|
+
*/
|
|
2580
|
+
const LogBuiltInMetadata = {
|
|
2581
|
+
// Contexts
|
|
2582
|
+
Context: {
|
|
2583
|
+
Uncategorized: 'n/a',
|
|
2584
|
+
ServerRenderedErrorPage: '_server-rendered-error-page',
|
|
2585
|
+
ServerEndpointError: '_server-endpoint-error',
|
|
2586
|
+
},
|
|
2587
|
+
// Targets
|
|
2588
|
+
Target: {
|
|
2589
|
+
NoSpecificTarget: 'n/a',
|
|
2590
|
+
},
|
|
2591
|
+
};
|
|
2592
|
+
|
|
2593
|
+
/**
|
|
2594
|
+
* Types of actions
|
|
2595
|
+
* @author Gabe Abrams
|
|
2596
|
+
*/
|
|
2597
|
+
var LogAction;
|
|
2598
|
+
(function (LogAction) {
|
|
2599
|
+
// Target was opened by the user (it was not on screen, but now it is)
|
|
2600
|
+
LogAction["Open"] = "open";
|
|
2601
|
+
// Target was closed by the user (it was on screen, but now it is not)
|
|
2602
|
+
LogAction["Close"] = "close";
|
|
2603
|
+
// Target was cancelled by the user (it was on closed without saving)
|
|
2604
|
+
LogAction["Cancel"] = "cancel";
|
|
2605
|
+
// Target was expanded by the user (it always remains on screen, but size was changed)
|
|
2606
|
+
LogAction["Expand"] = "expand";
|
|
2607
|
+
// Target was collapsed by the user (it always remains on screen, but size was changed)
|
|
2608
|
+
LogAction["Collapse"] = "collapse";
|
|
2609
|
+
// Target was viewed by the user (only for items that are not opened or closed, those must use Open/Close actions)
|
|
2610
|
+
LogAction["View"] = "view";
|
|
2611
|
+
// Target interrupted the user (popup, dialog, validation message, etc. appeared without user prompting)
|
|
2612
|
+
LogAction["Interrupt"] = "interrupt";
|
|
2613
|
+
// Target was created by the user (it did not exist before)
|
|
2614
|
+
LogAction["Create"] = "create";
|
|
2615
|
+
// Target was edited by the user (it existed and was changed)
|
|
2616
|
+
LogAction["Edit"] = "edit";
|
|
2617
|
+
// Target was deleted by the user (it existed and now it doesn't)
|
|
2618
|
+
LogAction["Delete"] = "delete";
|
|
2619
|
+
// Target was added by the user (it already existed and was added to another place)
|
|
2620
|
+
LogAction["Add"] = "add";
|
|
2621
|
+
// Target was removed by the user (it was removed from something but still exists)
|
|
2622
|
+
LogAction["Remove"] = "remove";
|
|
2623
|
+
// Target was activated by the user (click, check, tap, keypress, etc.)
|
|
2624
|
+
LogAction["Activate"] = "activate";
|
|
2625
|
+
// Target was deactivated by the user (click away, uncheck, tap outside of, tab away, etc.)
|
|
2626
|
+
LogAction["Deactivate"] = "deactivate";
|
|
2627
|
+
// User showed interest in a target (hover, peek, etc.)
|
|
2628
|
+
LogAction["Peek"] = "peek";
|
|
2629
|
+
// Unknown action
|
|
2630
|
+
LogAction["Unknown"] = "unknown";
|
|
2631
|
+
})(LogAction || (LogAction = {}));
|
|
2632
|
+
var LogAction$1 = LogAction;
|
|
2633
|
+
|
|
2361
2634
|
/**
|
|
2362
2635
|
* Generate an express API route handler
|
|
2363
2636
|
* @author Gabe Abrams
|
|
@@ -2673,6 +2946,146 @@ const genRouteHandler = (opts) => {
|
|
|
2673
2946
|
status: 401,
|
|
2674
2947
|
});
|
|
2675
2948
|
}
|
|
2949
|
+
/*----------------------------------------*/
|
|
2950
|
+
/* Log Handler */
|
|
2951
|
+
/*----------------------------------------*/
|
|
2952
|
+
// Create a log handler function
|
|
2953
|
+
/**
|
|
2954
|
+
* Log an event on the server
|
|
2955
|
+
* @author Gabe Abrams
|
|
2956
|
+
*/
|
|
2957
|
+
const logServerEvent = (opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
2958
|
+
var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
2959
|
+
// NOTE: internally, we slip through an opts.overrideAsClientEvent boolean
|
|
2960
|
+
// that indicates that this is actually a client event, but we don't
|
|
2961
|
+
// include that in the LogFunction type because this is internal and
|
|
2962
|
+
// hidden from users
|
|
2963
|
+
try {
|
|
2964
|
+
// Parse user agent
|
|
2965
|
+
const { browser, device, } = parseUserAgent(req.headers['user-agent']);
|
|
2966
|
+
// Get time info in ET
|
|
2967
|
+
const { timestamp, year, month, day, hour, minute, } = getTimeInfoInET();
|
|
2968
|
+
// Main log info
|
|
2969
|
+
const mainLogInfo = {
|
|
2970
|
+
id: `${launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
|
|
2971
|
+
userFirstName: launchInfo.userFirstName,
|
|
2972
|
+
userLastName: launchInfo.userLastName,
|
|
2973
|
+
userEmail: launchInfo.userEmail,
|
|
2974
|
+
userId: launchInfo.userId,
|
|
2975
|
+
isLearner: !!launchInfo.isLearner,
|
|
2976
|
+
isAdmin: !!launchInfo.isAdmin,
|
|
2977
|
+
isTTM: !!launchInfo.isTTM,
|
|
2978
|
+
courseId: launchInfo.courseId,
|
|
2979
|
+
courseName: launchInfo.courseName,
|
|
2980
|
+
browser,
|
|
2981
|
+
device,
|
|
2982
|
+
year,
|
|
2983
|
+
month,
|
|
2984
|
+
day,
|
|
2985
|
+
hour,
|
|
2986
|
+
minute,
|
|
2987
|
+
timestamp,
|
|
2988
|
+
context: (typeof opts.context === 'string'
|
|
2989
|
+
? opts.context
|
|
2990
|
+
: ((_d = ((_c = opts.context) !== null && _c !== void 0 ? _c : {})._) !== null && _d !== void 0 ? _d : LogBuiltInMetadata.Context.Uncategorized)),
|
|
2991
|
+
subcontext: (typeof opts.context === 'string'
|
|
2992
|
+
? opts.subcontext
|
|
2993
|
+
: ((_f = ((_e = opts.subcontext) !== null && _e !== void 0 ? _e : {})._) !== null && _f !== void 0 ? _f : LogBuiltInMetadata.Context.Uncategorized)),
|
|
2994
|
+
tags: (_g = opts.tags) !== null && _g !== void 0 ? _g : [],
|
|
2995
|
+
metadata: (_h = opts.metadata) !== null && _h !== void 0 ? _h : {},
|
|
2996
|
+
};
|
|
2997
|
+
// Type-specific info
|
|
2998
|
+
const typeSpecificInfo = (('error' in opts && opts.error)
|
|
2999
|
+
? {
|
|
3000
|
+
type: LogType$1.Error,
|
|
3001
|
+
errorMessage: (_j = opts.error.message) !== null && _j !== void 0 ? _j : 'Unknown message',
|
|
3002
|
+
errorCode: (_k = opts.error.code) !== null && _k !== void 0 ? _k : ReactKitErrorCode$1.NoCode,
|
|
3003
|
+
errorStack: (_l = opts.error.stack) !== null && _l !== void 0 ? _l : 'No stack',
|
|
3004
|
+
}
|
|
3005
|
+
: {
|
|
3006
|
+
type: LogType$1.Action,
|
|
3007
|
+
target: ((_m = opts.target) !== null && _m !== void 0 ? _m : LogBuiltInMetadata.Target.NoSpecificTarget),
|
|
3008
|
+
action: ((_o = opts.action) !== null && _o !== void 0 ? _o : LogAction$1.Unknown),
|
|
3009
|
+
});
|
|
3010
|
+
// Source-specific info
|
|
3011
|
+
const sourceSpecificInfo = (opts.overrideAsClientEvent
|
|
3012
|
+
? {
|
|
3013
|
+
source: LogSource$1.Client,
|
|
3014
|
+
}
|
|
3015
|
+
: {
|
|
3016
|
+
source: LogSource$1.Server,
|
|
3017
|
+
routePath: req.path,
|
|
3018
|
+
routeTemplate: req.route.path,
|
|
3019
|
+
});
|
|
3020
|
+
// Build log event
|
|
3021
|
+
const log = Object.assign(Object.assign(Object.assign({}, mainLogInfo), typeSpecificInfo), sourceSpecificInfo);
|
|
3022
|
+
// Either print to console or save to db
|
|
3023
|
+
const logCollection = internalGetLogCollection();
|
|
3024
|
+
if (logCollection) {
|
|
3025
|
+
// Store to the log collection
|
|
3026
|
+
yield logCollection.insert(log);
|
|
3027
|
+
}
|
|
3028
|
+
else {
|
|
3029
|
+
// Print to console
|
|
3030
|
+
if (log.type === LogType$1.Error) {
|
|
3031
|
+
console.error('dce-reactkit error log:', log);
|
|
3032
|
+
}
|
|
3033
|
+
else {
|
|
3034
|
+
console.log('dce-reactkit action log:', log);
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
// Return log entry
|
|
3038
|
+
return log;
|
|
3039
|
+
}
|
|
3040
|
+
catch (err) {
|
|
3041
|
+
// Print because we cannot store the error
|
|
3042
|
+
console.error('Could not log the following:', opts);
|
|
3043
|
+
// Create a dummy log to return
|
|
3044
|
+
const dummyMainInfo = {
|
|
3045
|
+
id: '-1',
|
|
3046
|
+
userFirstName: 'Unknown',
|
|
3047
|
+
userLastName: 'Unknown',
|
|
3048
|
+
userEmail: 'unknown@harvard.edu',
|
|
3049
|
+
userId: 1,
|
|
3050
|
+
isLearner: false,
|
|
3051
|
+
isAdmin: false,
|
|
3052
|
+
isTTM: false,
|
|
3053
|
+
courseId: 1,
|
|
3054
|
+
courseName: 'Unknown',
|
|
3055
|
+
browser: {
|
|
3056
|
+
name: 'Unknown',
|
|
3057
|
+
version: 'Unknown',
|
|
3058
|
+
},
|
|
3059
|
+
device: {
|
|
3060
|
+
isMobile: false,
|
|
3061
|
+
os: 'Unknown',
|
|
3062
|
+
},
|
|
3063
|
+
year: 1,
|
|
3064
|
+
month: 1,
|
|
3065
|
+
day: 1,
|
|
3066
|
+
hour: 1,
|
|
3067
|
+
minute: 1,
|
|
3068
|
+
timestamp: Date.now(),
|
|
3069
|
+
tags: [],
|
|
3070
|
+
metadata: {},
|
|
3071
|
+
context: LogBuiltInMetadata.Context.Uncategorized,
|
|
3072
|
+
subcontext: LogBuiltInMetadata.Context.Uncategorized,
|
|
3073
|
+
};
|
|
3074
|
+
const dummyTypeSpecificInfo = {
|
|
3075
|
+
type: LogType$1.Error,
|
|
3076
|
+
errorMessage: 'Unknown',
|
|
3077
|
+
errorCode: 'Unknown',
|
|
3078
|
+
errorStack: 'No Stack',
|
|
3079
|
+
};
|
|
3080
|
+
const dummySourceSpecificInfo = {
|
|
3081
|
+
source: LogSource$1.Server,
|
|
3082
|
+
routePath: req.path,
|
|
3083
|
+
routeTemplate: req.route.path,
|
|
3084
|
+
};
|
|
3085
|
+
const log = Object.assign(Object.assign(Object.assign({}, dummyMainInfo), dummyTypeSpecificInfo), dummySourceSpecificInfo);
|
|
3086
|
+
return log;
|
|
3087
|
+
}
|
|
3088
|
+
});
|
|
2676
3089
|
/*------------------------------------------------------------------------*/
|
|
2677
3090
|
/* Call handler */
|
|
2678
3091
|
/*------------------------------------------------------------------------*/
|
|
@@ -2710,9 +3123,24 @@ const genRouteHandler = (opts) => {
|
|
|
2710
3123
|
* @param [opts.status=500] http status code
|
|
2711
3124
|
*/
|
|
2712
3125
|
const renderErrorPage = (opts = {}) => {
|
|
2713
|
-
var _a;
|
|
3126
|
+
var _a, _b;
|
|
2714
3127
|
const html = genErrorPage(opts);
|
|
2715
3128
|
send(html, (_a = opts.status) !== null && _a !== void 0 ? _a : 500);
|
|
3129
|
+
// Log
|
|
3130
|
+
logServerEvent({
|
|
3131
|
+
context: LogBuiltInMetadata.Context.ServerRenderedErrorPage,
|
|
3132
|
+
error: {
|
|
3133
|
+
message: `${opts.title}: ${opts.description}`,
|
|
3134
|
+
code: opts.code,
|
|
3135
|
+
},
|
|
3136
|
+
metadata: {
|
|
3137
|
+
title: opts.title,
|
|
3138
|
+
description: opts.description,
|
|
3139
|
+
code: opts.code,
|
|
3140
|
+
pageTitle: opts.pageTitle,
|
|
3141
|
+
status: (_b = opts.status) !== null && _b !== void 0 ? _b : 500,
|
|
3142
|
+
},
|
|
3143
|
+
});
|
|
2716
3144
|
};
|
|
2717
3145
|
// Call the handler
|
|
2718
3146
|
try {
|
|
@@ -2726,6 +3154,7 @@ const genRouteHandler = (opts) => {
|
|
|
2726
3154
|
},
|
|
2727
3155
|
redirect,
|
|
2728
3156
|
renderErrorPage,
|
|
3157
|
+
logServerEvent,
|
|
2729
3158
|
});
|
|
2730
3159
|
// Send results to client (only if next wasn't called)
|
|
2731
3160
|
if (!responseSent) {
|
|
@@ -2735,7 +3164,13 @@ const genRouteHandler = (opts) => {
|
|
|
2735
3164
|
catch (err) {
|
|
2736
3165
|
// Send error to client (only if next wasn't called)
|
|
2737
3166
|
if (!responseSent) {
|
|
2738
|
-
|
|
3167
|
+
handleError(res, err);
|
|
3168
|
+
// Log server-side error
|
|
3169
|
+
logServerEvent({
|
|
3170
|
+
context: LogBuiltInMetadata.Context.ServerEndpointError,
|
|
3171
|
+
error: err,
|
|
3172
|
+
});
|
|
3173
|
+
return;
|
|
2739
3174
|
}
|
|
2740
3175
|
// Log error that was not responded with
|
|
2741
3176
|
console.log('Error occurred but could not be sent to client because a response was already sent:', err);
|
|
@@ -2929,6 +3364,61 @@ const parallelLimit = (taskFunctions, limit) => __awaiter(void 0, void 0, void 0
|
|
|
2929
3364
|
return results;
|
|
2930
3365
|
});
|
|
2931
3366
|
|
|
3367
|
+
/**
|
|
3368
|
+
* Log a user action on the client (cannot be used on the server)
|
|
3369
|
+
* @author Gabe Abrams
|
|
3370
|
+
*/
|
|
3371
|
+
const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3372
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
3373
|
+
return visitServerEndpoint({
|
|
3374
|
+
path: LOG_ROUTE_PATH,
|
|
3375
|
+
method: 'POST',
|
|
3376
|
+
params: {
|
|
3377
|
+
context: (typeof opts.context === 'string'
|
|
3378
|
+
? opts.context
|
|
3379
|
+
: ((_b = ((_a = opts.context) !== null && _a !== void 0 ? _a : {})._) !== null && _b !== void 0 ? _b : LogBuiltInMetadata.Context.Uncategorized)),
|
|
3380
|
+
subcontext: (typeof opts.context === 'string'
|
|
3381
|
+
? opts.subcontext
|
|
3382
|
+
: ((_d = ((_c = opts.subcontext) !== null && _c !== void 0 ? _c : {})._) !== null && _d !== void 0 ? _d : LogBuiltInMetadata.Context.Uncategorized)),
|
|
3383
|
+
tags: JSON.stringify((_e = opts.tags) !== null && _e !== void 0 ? _e : []),
|
|
3384
|
+
metadata: JSON.stringify((_f = opts.metadata) !== null && _f !== void 0 ? _f : {}),
|
|
3385
|
+
errorMessage: (opts.error
|
|
3386
|
+
? opts.error.message
|
|
3387
|
+
: undefined),
|
|
3388
|
+
errorCode: (opts.error
|
|
3389
|
+
? opts.error.code
|
|
3390
|
+
: undefined),
|
|
3391
|
+
errorStack: (opts.error
|
|
3392
|
+
? opts.error.stack
|
|
3393
|
+
: undefined),
|
|
3394
|
+
target: (opts.action
|
|
3395
|
+
? ((_g = opts.target) !== null && _g !== void 0 ? _g : LogBuiltInMetadata.Target.NoSpecificTarget)
|
|
3396
|
+
: undefined),
|
|
3397
|
+
action: (opts.action
|
|
3398
|
+
? opts.action
|
|
3399
|
+
: undefined),
|
|
3400
|
+
},
|
|
3401
|
+
});
|
|
3402
|
+
});
|
|
3403
|
+
|
|
3404
|
+
/**
|
|
3405
|
+
* Initialize a log collection given the dce-mango Collection class
|
|
3406
|
+
* @author Gabe Abrams
|
|
3407
|
+
* @param Collection the Collection class from dce-mango
|
|
3408
|
+
* @returns initialized logCollection
|
|
3409
|
+
*/
|
|
3410
|
+
const initLogCollection = (Collection) => {
|
|
3411
|
+
return new Collection('Log', {
|
|
3412
|
+
uniqueIndexKey: 'id',
|
|
3413
|
+
indexKeys: [
|
|
3414
|
+
'courseId',
|
|
3415
|
+
'context',
|
|
3416
|
+
'subcontext',
|
|
3417
|
+
'tags',
|
|
3418
|
+
],
|
|
3419
|
+
});
|
|
3420
|
+
};
|
|
3421
|
+
|
|
2932
3422
|
/**
|
|
2933
3423
|
* Days of the week
|
|
2934
3424
|
* @author Gabe Abrams
|
|
@@ -2945,5 +3435,5 @@ var DayOfWeek;
|
|
|
2945
3435
|
})(DayOfWeek || (DayOfWeek = {}));
|
|
2946
3436
|
var DayOfWeek$1 = DayOfWeek;
|
|
2947
3437
|
|
|
2948
|
-
export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek$1 as DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, Variant$1 as Variant, abbreviate, alert$1 as 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 };
|
|
3438
|
+
export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek$1 as DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, Variant$1 as Variant, abbreviate, alert$1 as alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, getHumanReadableDate, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initLogCollection, initServer, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
|
|
2949
3439
|
//# sourceMappingURL=index.js.map
|