dce-reactkit 3.2.1 → 3.2.2-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 +3 -0
- package/dist/cjs/index.js +2195 -442
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/ButtonInputGroup.d.ts +1 -0
- package/dist/cjs/types/components/CSVDownloadButton.d.ts +19 -0
- package/dist/cjs/types/components/IntelliTable.d.ts +17 -0
- package/dist/cjs/types/components/ItemPicker/index.d.ts +1 -0
- package/dist/cjs/types/components/LogReviewer.d.ts +13 -0
- package/dist/cjs/types/components/SimpleDateChooser.d.ts +1 -0
- package/dist/cjs/types/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +6 -0
- package/dist/cjs/types/constants/LOG_REVIEW_STATUS_ROUTE.d.ts +7 -0
- package/dist/cjs/types/helpers/canReviewLogs.d.ts +7 -0
- package/dist/cjs/types/helpers/genCSV.d.ts +14 -0
- package/dist/cjs/types/helpers/getMonthName.d.ts +14 -0
- package/dist/cjs/types/index.d.ts +9 -1
- package/dist/cjs/types/server/initServer.d.ts +8 -0
- package/dist/cjs/types/types/IntelliTableColumn.d.ts +12 -0
- package/dist/cjs/types/types/LogBuiltInMetadata.d.ts +1 -0
- package/dist/cjs/types/types/LogMetadataType.d.ts +19 -0
- package/dist/cjs/types/types/ReactKitErrorCode.d.ts +2 -1
- package/dist/esm/index.js +2191 -444
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/ButtonInputGroup.d.ts +1 -0
- package/dist/esm/types/components/CSVDownloadButton.d.ts +19 -0
- package/dist/esm/types/components/IntelliTable.d.ts +17 -0
- package/dist/esm/types/components/ItemPicker/index.d.ts +1 -0
- package/dist/esm/types/components/LogReviewer.d.ts +13 -0
- package/dist/esm/types/components/SimpleDateChooser.d.ts +1 -0
- package/dist/esm/types/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +6 -0
- package/dist/esm/types/constants/LOG_REVIEW_STATUS_ROUTE.d.ts +7 -0
- package/dist/esm/types/helpers/canReviewLogs.d.ts +7 -0
- package/dist/esm/types/helpers/genCSV.d.ts +14 -0
- package/dist/esm/types/helpers/getMonthName.d.ts +14 -0
- package/dist/esm/types/index.d.ts +9 -1
- package/dist/esm/types/server/initServer.d.ts +8 -0
- package/dist/esm/types/types/IntelliTableColumn.d.ts +12 -0
- package/dist/esm/types/types/LogBuiltInMetadata.d.ts +1 -0
- package/dist/esm/types/types/LogMetadataType.d.ts +19 -0
- package/dist/esm/types/types/ReactKitErrorCode.d.ts +2 -1
- package/dist/index.d.ts +172 -47
- package/package.json +1 -1
- package/sandbox.js +78 -17
- package/src/components/AppWrapper.tsx +15 -0
- package/src/components/ButtonInputGroup.tsx +4 -1
- package/src/components/CSVDownloadButton.tsx +102 -0
- package/src/components/IntelliTable.tsx +610 -0
- package/src/components/ItemPicker/index.tsx +4 -0
- package/src/components/LogReviewer.tsx +2091 -0
- package/src/components/SimpleDateChooser.tsx +26 -27
- package/src/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.ts +9 -0
- package/src/constants/LOG_REVIEW_STATUS_ROUTE.ts +10 -0
- package/src/helpers/canReviewLogs.ts +33 -0
- package/src/helpers/genCSV.ts +59 -0
- package/src/helpers/getHumanReadableDate.ts +6 -17
- package/src/helpers/getMonthName.ts +29 -0
- package/src/helpers/logClientEvent.tsx +5 -0
- package/src/index.ts +16 -0
- package/src/server/initServer.ts +125 -3
- package/src/types/IntelliTableColumn.ts +24 -0
- package/src/types/LogBuiltInMetadata.ts +1 -0
- package/src/types/LogMetadataType.ts +23 -0
- package/src/types/ReactKitErrorCode.tsx +2 -1
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
// Import React
|
|
7
7
|
import React from 'react';
|
|
8
|
+
import getMonthName from '../helpers/getMonthName';
|
|
8
9
|
|
|
9
10
|
// Import helpers
|
|
10
11
|
import getOrdinal from '../helpers/getOrdinal';
|
|
11
12
|
import getTimeInfoInET from '../helpers/getTimeInfoInET';
|
|
13
|
+
import forceNumIntoBounds from '../helpers/forceNumIntoBounds';
|
|
12
14
|
|
|
13
15
|
/*------------------------------------------------------------------------*/
|
|
14
16
|
/* Types */
|
|
@@ -32,31 +34,14 @@ type Props = {
|
|
|
32
34
|
* @param year new full year number
|
|
33
35
|
*/
|
|
34
36
|
onChange: (month: number, day: number, year: number) => void,
|
|
35
|
-
// Number of months
|
|
36
|
-
// (max is 12)
|
|
37
|
+
// Number of months to allow the user to choose from
|
|
38
|
+
// (max is 12, default is 6)
|
|
37
39
|
numMonthsToShow?: number,
|
|
40
|
+
// If true, instead of showing numMonthsToShow months into the future,
|
|
41
|
+
// show numMonthsToShow months into the past
|
|
42
|
+
chooseFromPast?: boolean,
|
|
38
43
|
};
|
|
39
44
|
|
|
40
|
-
/*------------------------------------------------------------------------*/
|
|
41
|
-
/* Constants */
|
|
42
|
-
/*------------------------------------------------------------------------*/
|
|
43
|
-
|
|
44
|
-
// Constants
|
|
45
|
-
const MONTH_NAMES = [
|
|
46
|
-
'January',
|
|
47
|
-
'February',
|
|
48
|
-
'March',
|
|
49
|
-
'April',
|
|
50
|
-
'May',
|
|
51
|
-
'June',
|
|
52
|
-
'July',
|
|
53
|
-
'August',
|
|
54
|
-
'September',
|
|
55
|
-
'October',
|
|
56
|
-
'November',
|
|
57
|
-
'December',
|
|
58
|
-
];
|
|
59
|
-
|
|
60
45
|
/*------------------------------------------------------------------------*/
|
|
61
46
|
/* Component */
|
|
62
47
|
/*------------------------------------------------------------------------*/
|
|
@@ -75,8 +60,13 @@ const SimpleDateChooser: React.FC<Props> = (props) => {
|
|
|
75
60
|
day,
|
|
76
61
|
year,
|
|
77
62
|
onChange,
|
|
63
|
+
chooseFromPast,
|
|
78
64
|
} = props;
|
|
79
|
-
|
|
65
|
+
let numMonthsToShow = forceNumIntoBounds(
|
|
66
|
+
(props.numMonthsToShow ?? 6),
|
|
67
|
+
1,
|
|
68
|
+
12,
|
|
69
|
+
);
|
|
80
70
|
|
|
81
71
|
/*------------------------------------------------------------------------*/
|
|
82
72
|
/* Render */
|
|
@@ -94,19 +84,28 @@ const SimpleDateChooser: React.FC<Props> = (props) => {
|
|
|
94
84
|
days: number[],
|
|
95
85
|
year: number,
|
|
96
86
|
}[] = [];
|
|
87
|
+
let startYear = today.year;
|
|
88
|
+
let startMonth = today.month;
|
|
89
|
+
if (chooseFromPast) {
|
|
90
|
+
startMonth -= Math.max(0, numMonthsToShow - 1);
|
|
91
|
+
if (startMonth <= 0) {
|
|
92
|
+
startMonth += 12;
|
|
93
|
+
startYear -= 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
97
96
|
for (let i = 0; i < numMonthsToShow; i++) {
|
|
98
97
|
// Get month and year info
|
|
99
|
-
const unmoddedMonth = (
|
|
98
|
+
const unmoddedMonth = (startMonth + i);
|
|
100
99
|
const month = (
|
|
101
100
|
unmoddedMonth > 12
|
|
102
101
|
? unmoddedMonth - 12
|
|
103
102
|
: unmoddedMonth
|
|
104
103
|
);
|
|
105
|
-
const monthName =
|
|
104
|
+
const monthName = getMonthName(month).full;
|
|
106
105
|
const year = (
|
|
107
106
|
unmoddedMonth > 12
|
|
108
|
-
?
|
|
109
|
-
:
|
|
107
|
+
? startYear + 1
|
|
108
|
+
: startYear
|
|
110
109
|
);
|
|
111
110
|
|
|
112
111
|
// Figure out which days are allowed
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import ROUTE_PATH_PREFIX from './ROUTE_PATH_PREFIX';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Route for checking the status of the current user's
|
|
5
|
+
* access to log review
|
|
6
|
+
* @author Gabe Abrams
|
|
7
|
+
*/
|
|
8
|
+
const LOG_REVIEW_STATUS_ROUTE = `${ROUTE_PATH_PREFIX}/logs/access_allowed`;
|
|
9
|
+
|
|
10
|
+
export default LOG_REVIEW_STATUS_ROUTE;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Import shared helpers
|
|
2
|
+
import visitServerEndpoint from './visitServerEndpoint';
|
|
3
|
+
|
|
4
|
+
// Import shared constants
|
|
5
|
+
import LOG_REVIEW_STATUS_ROUTE from '../constants/LOG_REVIEW_STATUS_ROUTE';
|
|
6
|
+
|
|
7
|
+
// Cache user's ability
|
|
8
|
+
let canReview: boolean | undefined = undefined;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check if the current user can review logs
|
|
12
|
+
* @author Gabe Abrams
|
|
13
|
+
* @returns true if current user can review logs
|
|
14
|
+
*/
|
|
15
|
+
const canReviewLogs = async (): Promise<boolean> => {
|
|
16
|
+
// If cached, use that value
|
|
17
|
+
if (canReview !== undefined) {
|
|
18
|
+
return canReview;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Ask on server
|
|
22
|
+
try {
|
|
23
|
+
canReview = !!(await visitServerEndpoint({
|
|
24
|
+
path: LOG_REVIEW_STATUS_ROUTE,
|
|
25
|
+
method: 'GET',
|
|
26
|
+
}));
|
|
27
|
+
} catch (err) {
|
|
28
|
+
canReview = false;
|
|
29
|
+
}
|
|
30
|
+
return canReview;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default canReviewLogs;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escape a CSV cell if needed
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
* @param text the cell contents
|
|
5
|
+
* @returns escaped cell text
|
|
6
|
+
*/
|
|
7
|
+
const escapeCellText = (text: string): string => {
|
|
8
|
+
if (!text.includes(',')) {
|
|
9
|
+
// No need to escape
|
|
10
|
+
return text;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Perform escape
|
|
14
|
+
return `"${text.replace(/"/g, '""')}`;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generate a CSV file
|
|
19
|
+
* @author Gabe Abrams
|
|
20
|
+
* @param data list of row data in the form of json objects
|
|
21
|
+
* @param columns list of columns to include in the csv
|
|
22
|
+
* @returns multiline csv string
|
|
23
|
+
*/
|
|
24
|
+
const genCSV = (
|
|
25
|
+
data: {
|
|
26
|
+
[k: string]: any
|
|
27
|
+
}[],
|
|
28
|
+
columns: {
|
|
29
|
+
title: string,
|
|
30
|
+
param: string,
|
|
31
|
+
}[],
|
|
32
|
+
): string => {
|
|
33
|
+
let csv = '';
|
|
34
|
+
|
|
35
|
+
// Add header
|
|
36
|
+
csv += (
|
|
37
|
+
columns
|
|
38
|
+
.map((column) => {
|
|
39
|
+
return escapeCellText(column.title);
|
|
40
|
+
})
|
|
41
|
+
.join(',')
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Add each row
|
|
45
|
+
data.forEach((datum) => {
|
|
46
|
+
csv += (
|
|
47
|
+
columns
|
|
48
|
+
.map((column) => {
|
|
49
|
+
return escapeCellText(datum[column.param]);
|
|
50
|
+
})
|
|
51
|
+
.join(',')
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Return
|
|
56
|
+
return csv;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default genCSV;
|
|
@@ -1,21 +1,7 @@
|
|
|
1
|
+
// Import shared helpers
|
|
1
2
|
import getTimeInfoInET from './getTimeInfoInET';
|
|
2
3
|
import getOrdinal from './getOrdinal';
|
|
3
|
-
|
|
4
|
-
// Map of month to three letter description
|
|
5
|
-
const monthMap = {
|
|
6
|
-
1: 'Jan',
|
|
7
|
-
2: 'Feb',
|
|
8
|
-
3: 'Mar',
|
|
9
|
-
4: 'Apr',
|
|
10
|
-
5: 'May',
|
|
11
|
-
6: 'Jun',
|
|
12
|
-
7: 'Jul',
|
|
13
|
-
8: 'Aug',
|
|
14
|
-
9: 'Sep',
|
|
15
|
-
10: 'Oct',
|
|
16
|
-
11: 'Nov',
|
|
17
|
-
12: 'Dec',
|
|
18
|
-
};
|
|
4
|
+
import getMonthName from './getMonthName';
|
|
19
5
|
|
|
20
6
|
/**
|
|
21
7
|
* Get a human-readable description of a date (all in ET)
|
|
@@ -32,8 +18,11 @@ const getHumanReadableDate = (dateOrTimestamp?: Date | number) => {
|
|
|
32
18
|
} = getTimeInfoInET(dateOrTimestamp);
|
|
33
19
|
const currYear = getTimeInfoInET().year;
|
|
34
20
|
|
|
21
|
+
// Get the short month description
|
|
22
|
+
const monthName = getMonthName(month).short;
|
|
23
|
+
|
|
35
24
|
// Create start of description
|
|
36
|
-
let description = `${
|
|
25
|
+
let description = `${monthName} ${day}${getOrdinal(day)}`;
|
|
37
26
|
|
|
38
27
|
// Add on year if it's different
|
|
39
28
|
if (year !== currYear) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const monthNames = [
|
|
2
|
+
{ short: 'Jan', full: 'January' },
|
|
3
|
+
{ short: 'Feb', full: 'February' },
|
|
4
|
+
{ short: 'Mar', full: 'March' },
|
|
5
|
+
{ short: 'Apr', full: 'April' },
|
|
6
|
+
{ short: 'May', full: 'May' },
|
|
7
|
+
{ short: 'Jun', full: 'June' },
|
|
8
|
+
{ short: 'Jul', full: 'July' },
|
|
9
|
+
{ short: 'Aug', full: 'August' },
|
|
10
|
+
{ short: 'Sep', full: 'September' },
|
|
11
|
+
{ short: 'Oct', full: 'October' },
|
|
12
|
+
{ short: 'Nov', full: 'November' },
|
|
13
|
+
{ short: 'Dec', full: 'December' },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get the name of a month given the month number (1 = January, etc.)
|
|
18
|
+
* If an invalid number is provided, we will treat it like January
|
|
19
|
+
* @author Gabe Abrams
|
|
20
|
+
* @param month the number of the month
|
|
21
|
+
* @returns object containing multiple month name formats:
|
|
22
|
+
* { short, full } where short will look like "Jan" and full will look like
|
|
23
|
+
* "January"
|
|
24
|
+
*/
|
|
25
|
+
const getMonthName = (month: number) => {
|
|
26
|
+
return (monthNames[month - 1] ?? monthNames[0]);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default getMonthName;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import LOG_ROUTE_PATH from '../constants/LOG_ROUTE_PATH';
|
|
3
3
|
import LogBuiltInMetadata from '../types/LogBuiltInMetadata';
|
|
4
4
|
import LogFunction from '../types/LogFunction';
|
|
5
|
+
import LogLevel from '../types/LogLevel';
|
|
5
6
|
|
|
6
7
|
// Import shared functions
|
|
7
8
|
import visitServerEndpoint from './visitServerEndpoint';
|
|
@@ -27,6 +28,10 @@ const logClientEvent: LogFunction = async (opts) => {
|
|
|
27
28
|
opts.subcontext
|
|
28
29
|
?? LogBuiltInMetadata.Context.Uncategorized
|
|
29
30
|
),
|
|
31
|
+
level: (
|
|
32
|
+
opts.level
|
|
33
|
+
?? LogLevel.Info
|
|
34
|
+
),
|
|
30
35
|
tags: JSON.stringify(opts.tags ?? []),
|
|
31
36
|
metadata: JSON.stringify(opts.metadata ?? {}),
|
|
32
37
|
errorMessage: (
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,9 @@ import PopFailureMark from './components/PopFailureMark';
|
|
|
14
14
|
import PopPendingMark from './components/PopPendingMark';
|
|
15
15
|
import CopiableBox from './components/CopiableBox';
|
|
16
16
|
import ItemPicker from './components/ItemPicker';
|
|
17
|
+
import LogReviewer from './components/LogReviewer';
|
|
18
|
+
import IntelliTable from './components/IntelliTable';
|
|
19
|
+
import CSVDownloadButton from './components/CSVDownloadButton';
|
|
17
20
|
|
|
18
21
|
// Import errors
|
|
19
22
|
import ErrorWithCode from './errors/ErrorWithCode';
|
|
@@ -50,6 +53,9 @@ import onlyKeepLetters from './helpers/onlyKeepLetters';
|
|
|
50
53
|
import parallelLimit from './helpers/parallelLimit';
|
|
51
54
|
import logClientEvent from './helpers/logClientEvent';
|
|
52
55
|
import initLogCollection from './server/initLogCollection';
|
|
56
|
+
import getMonthName from './helpers/getMonthName';
|
|
57
|
+
import genCSV from './helpers/genCSV';
|
|
58
|
+
import canReviewLogs from './helpers/canReviewLogs';
|
|
53
59
|
|
|
54
60
|
// Import types
|
|
55
61
|
import ModalButtonType from './types/ModalButtonType';
|
|
@@ -64,6 +70,8 @@ import LogType from './types/LogType';
|
|
|
64
70
|
import LogSource from './types/LogSource';
|
|
65
71
|
import LogAction from './types/LogAction';
|
|
66
72
|
import LogBuiltInMetadata from './types/LogBuiltInMetadata';
|
|
73
|
+
import LogMetadataType from './types/LogMetadataType';
|
|
74
|
+
import IntelliTableColumn from './types/IntelliTableColumn';
|
|
67
75
|
|
|
68
76
|
// Component-specific-types
|
|
69
77
|
import PickableItem from './components/ItemPicker/types/PickableItem';
|
|
@@ -86,6 +94,9 @@ export {
|
|
|
86
94
|
PopPendingMark,
|
|
87
95
|
CopiableBox,
|
|
88
96
|
ItemPicker,
|
|
97
|
+
LogReviewer,
|
|
98
|
+
IntelliTable,
|
|
99
|
+
CSVDownloadButton,
|
|
89
100
|
// Global functions
|
|
90
101
|
alert,
|
|
91
102
|
confirm,
|
|
@@ -116,6 +127,9 @@ export {
|
|
|
116
127
|
stringsToHumanReadableList,
|
|
117
128
|
onlyKeepLetters,
|
|
118
129
|
parallelLimit,
|
|
130
|
+
getMonthName,
|
|
131
|
+
genCSV,
|
|
132
|
+
canReviewLogs,
|
|
119
133
|
// Client helpers
|
|
120
134
|
visitServerEndpoint,
|
|
121
135
|
logClientEvent,
|
|
@@ -137,6 +151,8 @@ export {
|
|
|
137
151
|
LogSource,
|
|
138
152
|
LogAction,
|
|
139
153
|
LogBuiltInMetadata,
|
|
154
|
+
LogMetadataType,
|
|
155
|
+
IntelliTableColumn,
|
|
140
156
|
// Component-specific-types
|
|
141
157
|
PickableItem,
|
|
142
158
|
// Server types
|
package/src/server/initServer.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
// Import custom error
|
|
2
|
-
import LOG_ROUTE_PATH from '../constants/LOG_ROUTE_PATH';
|
|
3
2
|
import ErrorWithCode from '../errors/ErrorWithCode';
|
|
4
3
|
|
|
4
|
+
// Import shared constants
|
|
5
|
+
import LOG_REVIEW_ROUTE_PATH_PREFIX from '../constants/LOG_REVIEW_ROUTE_PATH_PREFIX';
|
|
6
|
+
import LOG_ROUTE_PATH from '../constants/LOG_ROUTE_PATH';
|
|
7
|
+
import LOG_REVIEW_STATUS_ROUTE from '../constants/LOG_REVIEW_STATUS_ROUTE';
|
|
8
|
+
|
|
5
9
|
// Import shared helpers
|
|
6
10
|
import genRouteHandler from '../helpers/genRouteHandler';
|
|
7
|
-
import LogFunction from '../types/LogFunction';
|
|
8
|
-
import ParamType from '../types/ParamType';
|
|
9
11
|
|
|
10
12
|
// Import shared types
|
|
13
|
+
import Log from '../types/Log';
|
|
14
|
+
import LogFunction from '../types/LogFunction';
|
|
15
|
+
import ParamType from '../types/ParamType';
|
|
11
16
|
import ReactKitErrorCode from '../types/ReactKitErrorCode';
|
|
12
17
|
|
|
13
18
|
// Types
|
|
@@ -66,17 +71,29 @@ export const internalGetLogCollection = () => {
|
|
|
66
71
|
* @param opts.getLaunchInfo CACCL LTI's get launch info function
|
|
67
72
|
* @param [opts.logCollection] mongo collection from dce-mango to use for
|
|
68
73
|
* storing logs. If none is included, logs are written to the console
|
|
74
|
+
* @param [opts.logReviewAdmins=all] info on which admins can review
|
|
75
|
+
* logs from the client. If not included, all Canvas admins are allowed to
|
|
76
|
+
* review logs. If null, no Canvas admins are allowed to review logs.
|
|
77
|
+
* If an array of Canvas userIds (numbers), only Canvas admins with those
|
|
78
|
+
* userIds are allowed to review logs. If a dce-mango collection, only
|
|
79
|
+
* Canvas admins with entries in that collection ({ userId, ...}) are allowed
|
|
80
|
+
* to review logs
|
|
69
81
|
*/
|
|
70
82
|
const initServer = (
|
|
71
83
|
opts: {
|
|
72
84
|
app: any,
|
|
73
85
|
getLaunchInfo: GetLaunchInfoFunction,
|
|
74
86
|
logCollection?: any,
|
|
87
|
+
logReviewAdmins?: (number[] | any),
|
|
75
88
|
},
|
|
76
89
|
) => {
|
|
77
90
|
_cacclGetLaunchInfo = opts.getLaunchInfo;
|
|
78
91
|
_logCollection = opts.logCollection;
|
|
79
92
|
|
|
93
|
+
/*----------------------------------------*/
|
|
94
|
+
/* Logging */
|
|
95
|
+
/*----------------------------------------*/
|
|
96
|
+
|
|
80
97
|
/**
|
|
81
98
|
* Log an event
|
|
82
99
|
* @author Gabe Abrams
|
|
@@ -154,6 +171,111 @@ const initServer = (
|
|
|
154
171
|
},
|
|
155
172
|
}),
|
|
156
173
|
);
|
|
174
|
+
|
|
175
|
+
/*----------------------------------------*/
|
|
176
|
+
/* Log Reviewer */
|
|
177
|
+
/*----------------------------------------*/
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Check if a given user is allowed to review logs
|
|
181
|
+
* @author Gabe Abrams
|
|
182
|
+
* @param userId the id of the user
|
|
183
|
+
* @param isAdmin if true, the user is an admin
|
|
184
|
+
* @returns true if the user can review logs
|
|
185
|
+
*/
|
|
186
|
+
const canReviewLogs = async (
|
|
187
|
+
userId: number,
|
|
188
|
+
isAdmin: boolean,
|
|
189
|
+
): Promise<boolean> => {
|
|
190
|
+
// Immediately deny access if user is not an admin
|
|
191
|
+
if (!isAdmin) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// If all admins are allowed, we're done
|
|
196
|
+
if (!opts.logReviewAdmins) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Do a dynamic check
|
|
201
|
+
try {
|
|
202
|
+
// Array of userIds
|
|
203
|
+
if (Array.isArray(opts.logReviewAdmins)) {
|
|
204
|
+
return opts.logReviewAdmins.some((allowedId) => {
|
|
205
|
+
return (userId === allowedId);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Must be a collection
|
|
210
|
+
const matches = await opts.logReviewAdmins.find({ userId });
|
|
211
|
+
|
|
212
|
+
// Make sure at least one entry matches
|
|
213
|
+
return matches.length > 0;
|
|
214
|
+
} catch (err) {
|
|
215
|
+
// If an error occurred, simply return false
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Check if the current user has access to logs
|
|
222
|
+
* @author Gabe Abrams
|
|
223
|
+
* @returns {boolean} true if user has access
|
|
224
|
+
*/
|
|
225
|
+
opts.app.get(
|
|
226
|
+
LOG_REVIEW_STATUS_ROUTE,
|
|
227
|
+
genRouteHandler({
|
|
228
|
+
handler: async ({ params }) => {
|
|
229
|
+
const { userId, isAdmin } = params;
|
|
230
|
+
const canReview = await canReviewLogs(userId, isAdmin);
|
|
231
|
+
return canReview;
|
|
232
|
+
},
|
|
233
|
+
}),
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get all logs for a certain month
|
|
238
|
+
* @author Gabe Abrams
|
|
239
|
+
* @param {number} year the year to query (e.g. 2022)
|
|
240
|
+
* @param {number} month the month to query (e.g. 1 = January)
|
|
241
|
+
* @returns {Log[]} list of logs from the given month
|
|
242
|
+
*/
|
|
243
|
+
opts.app.get(
|
|
244
|
+
`${LOG_REVIEW_ROUTE_PATH_PREFIX}/years/:year/months/:month`,
|
|
245
|
+
genRouteHandler({
|
|
246
|
+
paramTypes: {
|
|
247
|
+
year: ParamType.Int,
|
|
248
|
+
month: ParamType.Int,
|
|
249
|
+
},
|
|
250
|
+
handler: async ({ params }) => {
|
|
251
|
+
// Get user info
|
|
252
|
+
const {
|
|
253
|
+
year,
|
|
254
|
+
month,
|
|
255
|
+
userId,
|
|
256
|
+
isAdmin,
|
|
257
|
+
} = params;
|
|
258
|
+
|
|
259
|
+
// Validate user
|
|
260
|
+
const canReview = await canReviewLogs(userId, isAdmin);
|
|
261
|
+
if (!canReview) {
|
|
262
|
+
throw new ErrorWithCode(
|
|
263
|
+
'You cannot access this resource because you do not have the appropriate permissions.',
|
|
264
|
+
ReactKitErrorCode.NotAllowedToReviewLogs,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Query for logs
|
|
269
|
+
const logs: Log[] = await _logCollection.find({
|
|
270
|
+
year,
|
|
271
|
+
month,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Return logs
|
|
275
|
+
return logs;
|
|
276
|
+
},
|
|
277
|
+
}),
|
|
278
|
+
);
|
|
157
279
|
};
|
|
158
280
|
|
|
159
281
|
export default initServer;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import ParamType from './ParamType';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Column description for a column in the IntelliTable
|
|
5
|
+
* @author Gabe Abrams
|
|
6
|
+
*/
|
|
7
|
+
type IntelliTableColumn = {
|
|
8
|
+
// Human-readable name of the column
|
|
9
|
+
title: string,
|
|
10
|
+
// Parameter name (property inside the data object)
|
|
11
|
+
param: string,
|
|
12
|
+
// Type of value
|
|
13
|
+
type: (
|
|
14
|
+
| ParamType.Boolean
|
|
15
|
+
| ParamType.Float
|
|
16
|
+
| ParamType.Int
|
|
17
|
+
| ParamType.String
|
|
18
|
+
| ParamType.JSON
|
|
19
|
+
),
|
|
20
|
+
// If true, column starts hidden
|
|
21
|
+
startsHidden?: boolean,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default IntelliTableColumn;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type of a LogMetadata file
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
type LogMetadataType = {
|
|
6
|
+
Context?: {
|
|
7
|
+
[k: string]: (
|
|
8
|
+
| string
|
|
9
|
+
| {
|
|
10
|
+
_: string,
|
|
11
|
+
[k: string]: string
|
|
12
|
+
}
|
|
13
|
+
)
|
|
14
|
+
},
|
|
15
|
+
Tag?: {
|
|
16
|
+
[k: string]: string
|
|
17
|
+
},
|
|
18
|
+
Target?: {
|
|
19
|
+
[k: string]: string
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default LogMetadataType;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Highest error code =
|
|
1
|
+
// Highest error code = DRK11
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* List of error codes built into the react kit
|
|
@@ -15,6 +15,7 @@ enum ReactKitErrorCode {
|
|
|
15
15
|
NoCACCLGetLaunchInfoFunction = 'DRK8',
|
|
16
16
|
NotTTM = 'DRK9',
|
|
17
17
|
NotAdmin = 'DRK10',
|
|
18
|
+
NotAllowedToReviewLogs = 'DRK11',
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export default ReactKitErrorCode;
|