studiokit-scaffolding-js 5.2.0-next.2.7 → 5.2.0-next.2.9
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/lib/components/UserRoles/index.d.ts +1 -1
- package/lib/redux/actions/AuthAction.d.ts +2 -3
- package/lib/redux/sagas/authSaga.js +15 -19
- package/lib/redux/sagas/modelFetchSaga.js +1 -1
- package/lib/redux/sagas/noStoreSaga.d.ts +4 -4
- package/lib/redux/sagas/noStoreSaga.js +6 -6
- package/lib/utils/quill.js +47 -33
- package/package.json +1 -1
|
@@ -96,7 +96,7 @@ export declare class UserRoles extends Component<UserRolesProps, UserRolesState>
|
|
|
96
96
|
textForRole: (role: string, lowercase?: boolean) => string;
|
|
97
97
|
singularArticleForRole: (role: string) => string;
|
|
98
98
|
addUserRoles: (identifiers: string[], role: string) => void;
|
|
99
|
-
didAdd: (data
|
|
99
|
+
didAdd: (data: AddBusinessModel | FetchErrorData | null) => void;
|
|
100
100
|
updateUserRole: (userRoleToUpdate: UserRole, newRole: string) => void;
|
|
101
101
|
didUpdate: (isSuccess: boolean) => void;
|
|
102
102
|
alertRemoveUserRole: (userRoleToRemove: UserRole) => void;
|
|
@@ -2,7 +2,6 @@ import { Action } from 'redux';
|
|
|
2
2
|
import { CasV1LoginRequestBody, IdentityProvider } from '../../types';
|
|
3
3
|
import { LocalLoginRequestBody } from '../../types/auth/LocalLoginRequestBody';
|
|
4
4
|
import { OAuthToken, OAuthTokenOrNull } from '../../types/net';
|
|
5
|
-
import { BaseModelFetchRequestAction } from './ModelAction';
|
|
6
5
|
export declare enum AUTH_ACTION_TYPE {
|
|
7
6
|
CAS_LOGIN_REQUESTED = "auth/CAS_LOGIN_REQUESTED",
|
|
8
7
|
LOGIN_REQUESTED = "auth/LOGIN_REQUESTED",
|
|
@@ -43,12 +42,12 @@ export interface AuthIdentityProviderRequestAction extends Action<AUTH_IDENTITY_
|
|
|
43
42
|
export declare enum AUTH_CAS_V1_LOGIN_REQUEST_ACTION_TYPE {
|
|
44
43
|
CAS_V1_LOGIN_REQUEST = "auth/CAS_V1_LOGIN_REQUEST"
|
|
45
44
|
}
|
|
46
|
-
export interface AuthCasV1LoginRequestAction extends
|
|
45
|
+
export interface AuthCasV1LoginRequestAction extends Action<AUTH_CAS_V1_LOGIN_REQUEST_ACTION_TYPE> {
|
|
47
46
|
body: CasV1LoginRequestBody;
|
|
48
47
|
}
|
|
49
48
|
export declare enum AUTH_LOCAL_LOGIN_REQUEST_ACTION_TYPE {
|
|
50
49
|
LOCAL_LOGIN_REQUEST = "auth/LOCAL_LOGIN_REQUEST"
|
|
51
50
|
}
|
|
52
|
-
export interface AuthLocalLoginRequestAction extends
|
|
51
|
+
export interface AuthLocalLoginRequestAction extends Action<AUTH_LOCAL_LOGIN_REQUEST_ACTION_TYPE> {
|
|
53
52
|
body: LocalLoginRequestBody;
|
|
54
53
|
}
|
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __assign = (this && this.__assign) || function () {
|
|
3
|
-
__assign = Object.assign || function(t) {
|
|
4
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
-
s = arguments[i];
|
|
6
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
-
t[p] = s[p];
|
|
8
|
-
}
|
|
9
|
-
return t;
|
|
10
|
-
};
|
|
11
|
-
return __assign.apply(this, arguments);
|
|
12
|
-
};
|
|
13
2
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
14
3
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
15
4
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
@@ -236,9 +225,16 @@ function credentialsLoginFlow(action, modelName) {
|
|
|
236
225
|
return __generator(this, function (_a) {
|
|
237
226
|
switch (_a.label) {
|
|
238
227
|
case 0:
|
|
239
|
-
modelFetchRequestAction =
|
|
240
|
-
// set required
|
|
241
|
-
type: actions_1.MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST,
|
|
228
|
+
modelFetchRequestAction = {
|
|
229
|
+
// set required defaults
|
|
230
|
+
type: actions_1.MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST,
|
|
231
|
+
modelName: modelName,
|
|
232
|
+
noStore: true,
|
|
233
|
+
// force no retry
|
|
234
|
+
noRetry: true,
|
|
235
|
+
// pass thru body
|
|
236
|
+
body: action.body
|
|
237
|
+
};
|
|
242
238
|
return [4 /*yield*/, effects_1.call(loginFlow, modelFetchRequestAction)];
|
|
243
239
|
case 1: return [2 /*return*/, _a.sent()];
|
|
244
240
|
}
|
|
@@ -323,7 +319,7 @@ var getOAuthToken = function (modelName) {
|
|
|
323
319
|
};
|
|
324
320
|
exports.getOAuthToken = getOAuthToken;
|
|
325
321
|
function authSaga(clientCredentialsParam, tokenPersistenceServiceParam, ticketProviderService, codeProviderService) {
|
|
326
|
-
var casTicket, service, code, _a, casV1Action,
|
|
322
|
+
var casTicket, service, code, _a, casV1Action, localLoginAction;
|
|
327
323
|
if (tokenPersistenceServiceParam === void 0) { tokenPersistenceServiceParam = tokenPersistenceService_1.tokenPersistenceService; }
|
|
328
324
|
if (ticketProviderService === void 0) { ticketProviderService = ticketProviderService_1.ticketProviderService; }
|
|
329
325
|
if (codeProviderService === void 0) { codeProviderService = codeProviderService_1.codeProviderService; }
|
|
@@ -381,10 +377,10 @@ function authSaga(clientCredentialsParam, tokenPersistenceServiceParam, ticketPr
|
|
|
381
377
|
if (!!oauthToken) return [3 /*break*/, 16];
|
|
382
378
|
return [4 /*yield*/, effects_1.race({
|
|
383
379
|
casV1Action: effects_1.take(actions_1.AUTH_CAS_V1_LOGIN_REQUEST_ACTION_TYPE.CAS_V1_LOGIN_REQUEST),
|
|
384
|
-
|
|
380
|
+
localLoginAction: effects_1.take(actions_1.AUTH_LOCAL_LOGIN_REQUEST_ACTION_TYPE.LOCAL_LOGIN_REQUEST)
|
|
385
381
|
})];
|
|
386
382
|
case 11:
|
|
387
|
-
_a = _b.sent(), casV1Action = _a.casV1Action,
|
|
383
|
+
_a = _b.sent(), casV1Action = _a.casV1Action, localLoginAction = _a.localLoginAction;
|
|
388
384
|
return [4 /*yield*/, effects_1.put({ type: actions_1.AUTH_ACTION_TYPE.LOGIN_REQUESTED })];
|
|
389
385
|
case 12:
|
|
390
386
|
_b.sent();
|
|
@@ -394,8 +390,8 @@ function authSaga(clientCredentialsParam, tokenPersistenceServiceParam, ticketPr
|
|
|
394
390
|
oauthToken = _b.sent();
|
|
395
391
|
return [3 /*break*/, 16];
|
|
396
392
|
case 14:
|
|
397
|
-
if (!
|
|
398
|
-
return [4 /*yield*/, effects_1.call(localLoginFlow,
|
|
393
|
+
if (!localLoginAction) return [3 /*break*/, 16];
|
|
394
|
+
return [4 /*yield*/, effects_1.call(localLoginFlow, localLoginAction)];
|
|
399
395
|
case 15:
|
|
400
396
|
oauthToken = _b.sent();
|
|
401
397
|
_b.label = 16;
|
|
@@ -308,7 +308,7 @@ function modelFetch(modelFetchRequestAction) {
|
|
|
308
308
|
// log to the console
|
|
309
309
|
logger.error(error);
|
|
310
310
|
didFail = true;
|
|
311
|
-
// Do not
|
|
311
|
+
// Do not retry if the response is between 400-500 (except 408: request timeout)
|
|
312
312
|
if (errorData.status >= types_1.HTTP_STATUS_CODE.BAD_REQUEST &&
|
|
313
313
|
errorData.status < types_1.HTTP_STATUS_CODE.INTERNAL_SERVER_ERROR &&
|
|
314
314
|
errorData.status !== types_1.HTTP_STATUS_CODE.REQUEST_TIMEOUT) {
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { SagaIterator } from '@redux-saga/core';
|
|
2
2
|
import { AnyAction } from 'redux';
|
|
3
|
+
import { FetchErrorData } from '../../types';
|
|
3
4
|
import { ModelFetchRequestAction } from '../actions';
|
|
4
|
-
declare type HookFunction = (input:
|
|
5
|
+
export declare type HookFunction<T = any> = (input: T | FetchErrorData | null) => void;
|
|
5
6
|
export declare const matchesNoStoreAction: (incomingAction: AnyAction) => incomingAction is ModelFetchRequestAction;
|
|
6
7
|
export declare const matchesFailedNoStoreHookAction: (incomingAction: AnyAction, noStoreAction: ModelFetchRequestAction) => boolean;
|
|
7
8
|
export declare const takeMatchesFailedNoStoreHookAction: (noStoreAction: ModelFetchRequestAction) => (incomingAction: AnyAction) => boolean;
|
|
8
9
|
export declare const matchesReceivedNoStoreHookAction: (incomingAction: AnyAction, noStoreAction: ModelFetchRequestAction) => boolean;
|
|
9
10
|
export declare const takeMatchesReceivedNoStoreHookAction: (noStoreAction: ModelFetchRequestAction) => (incomingAction: AnyAction) => boolean;
|
|
10
|
-
export declare const registerNoStoreActionHook: (key: string, hook: HookFunction) => void;
|
|
11
|
+
export declare const registerNoStoreActionHook: <T>(key: string, hook: HookFunction<T>) => void;
|
|
11
12
|
export declare const unregisterNoStoreActionHook: (key: string) => void;
|
|
12
13
|
export declare const noStoreHooks: {
|
|
13
|
-
registerNoStoreActionHook: (key: string, hook: HookFunction) => void;
|
|
14
|
+
registerNoStoreActionHook: <T>(key: string, hook: HookFunction<T>) => void;
|
|
14
15
|
unregisterNoStoreActionHook: (key: string) => void;
|
|
15
16
|
};
|
|
16
17
|
export declare function handleAction(noStoreAction: ModelFetchRequestAction): SagaIterator;
|
|
17
18
|
export default function noStoreSaga(): SagaIterator;
|
|
18
|
-
export {};
|
|
@@ -89,15 +89,15 @@ function handleAction(noStoreAction) {
|
|
|
89
89
|
if (lodash_1.isNil(hook)) {
|
|
90
90
|
return [2 /*return*/];
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
// return `errorData` on failure. modelFetchSaga will ALWAYS create `errorData`
|
|
93
|
+
if (errorAction) {
|
|
93
94
|
hook(errorAction.errorData);
|
|
94
95
|
return [2 /*return*/];
|
|
95
96
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
hook(resultAction.data);
|
|
97
|
+
// return `data` or `null` on a successful fetch result
|
|
98
|
+
// `data` can technically be `undefined`, e.g. if the server response is a NoContent (204),
|
|
99
|
+
// so the caller will need to determine if `null` is treated as an error or success
|
|
100
|
+
hook((resultAction === null || resultAction === void 0 ? void 0 : resultAction.data) || null);
|
|
101
101
|
return [2 /*return*/];
|
|
102
102
|
}
|
|
103
103
|
});
|
package/lib/utils/quill.js
CHANGED
|
@@ -19,6 +19,7 @@ var quill_1 = __importDefault(require("quill"));
|
|
|
19
19
|
var uuid_1 = require("uuid");
|
|
20
20
|
var actionCreator_1 = require("../redux/actionCreator");
|
|
21
21
|
var noStoreSaga_1 = require("../redux/sagas/noStoreSaga");
|
|
22
|
+
var types_1 = require("../types");
|
|
22
23
|
var Delta = quill_1.default.import('delta');
|
|
23
24
|
/**
|
|
24
25
|
* Check for non-public in the provided quill contents. Update the ImageValue of an op if an image is found to be
|
|
@@ -31,40 +32,53 @@ var checkForNonPublicImages = function (quill, checkedImages) {
|
|
|
31
32
|
var _a, _b;
|
|
32
33
|
(_b = (_a = quill
|
|
33
34
|
.getContents()) === null || _a === void 0 ? void 0 : _a.ops // Is the op an image and have we not seen it before?
|
|
34
|
-
) === null || _b === void 0 ? void 0 : _b.filter(function (op) {
|
|
35
|
+
) === null || _b === void 0 ? void 0 : _b.filter(function (op) {
|
|
36
|
+
return op.insert.image &&
|
|
37
|
+
!checkedImages.find(function (image) { return image.src === op.insert.image.src; }) &&
|
|
38
|
+
op.insert.image.src.startsWith('http');
|
|
39
|
+
}).forEach(function (op) {
|
|
35
40
|
// Check if the image is public. Do not check data:image urls
|
|
36
|
-
if
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
noStoreSaga_1.noStoreHooks.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
}
|
|
41
|
+
// Use the backend to check if the image is public
|
|
42
|
+
// Due to CSP and other security measures, we can't check this in the frontend
|
|
43
|
+
// uuidv5 is a deterministic uuid generator based on a namespace and a name
|
|
44
|
+
// uuidv5.URL is the namespace for urls
|
|
45
|
+
var hookId = uuid_1.v5(op.insert.image.src, uuid_1.v5.URL);
|
|
46
|
+
noStoreSaga_1.noStoreHooks.registerNoStoreActionHook(hookId, function (data) {
|
|
47
|
+
noStoreSaga_1.noStoreHooks.unregisterNoStoreActionHook(hookId);
|
|
48
|
+
var contentTypeKey;
|
|
49
|
+
/**
|
|
50
|
+
* API fetches the URL, and `noStoreSaga` returns either the result or error
|
|
51
|
+
* * `data` is `null` when the API could not fetch the URL with a successful status code, and returns `null` => `nonPublic`
|
|
52
|
+
* * `data` is `FetchErrorData` if the API request itself failed, e.g. from some unknown error, we don't know if the image is public => do not update
|
|
53
|
+
* * `data` is a dictionary of headers when the API does fetch the URL
|
|
54
|
+
* * if there is no `Content-Type` header, we don't know if the image is public => do not update
|
|
55
|
+
* * if there is a `Content-Type` header, but is NOT an image type (i.e. it was a 302 that, when followed returned HTML)
|
|
56
|
+
* (I'm looking at you, Brightspace) => `nonPublic`
|
|
57
|
+
* * if there is a `Content-Type` header, and it is an image type, then the image is public => do not update
|
|
58
|
+
*/
|
|
59
|
+
if (!data ||
|
|
60
|
+
(!types_1.isFetchErrorData(data) &&
|
|
61
|
+
(contentTypeKey = Object.keys(data).find(function (k) { return k.toLowerCase() === 'content-type'; })) &&
|
|
62
|
+
!data[contentTypeKey][0].startsWith('image'))) {
|
|
63
|
+
var contents = quill.getContents();
|
|
64
|
+
var ops = contents.map(function (o) {
|
|
65
|
+
var _a;
|
|
66
|
+
return ((_a = o.insert.image) === null || _a === void 0 ? void 0 : _a.src) && op.insert.image.src && o.insert.image.src === op.insert.image.src
|
|
67
|
+
? __assign(__assign({}, o), { insert: __assign(__assign({}, o.insert), { image: __assign(__assign({}, o.insert.image), { nonPublic: true }) }) }) : o;
|
|
68
|
+
});
|
|
69
|
+
var updatedContents = contents.diff(new Delta(ops));
|
|
70
|
+
quill.updateContents(updatedContents);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
// This endpoint returns headers for a url. If the url is nonexistent, it will return null
|
|
74
|
+
actionCreator_1.dispatchModelFetchRequest({
|
|
75
|
+
modelName: 'urlChecker',
|
|
76
|
+
guid: hookId,
|
|
77
|
+
noStore: true,
|
|
78
|
+
queryParams: {
|
|
79
|
+
url: op.insert.image.src
|
|
80
|
+
}
|
|
81
|
+
});
|
|
68
82
|
});
|
|
69
83
|
};
|
|
70
84
|
exports.checkForNonPublicImages = checkForNonPublicImages;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "studiokit-scaffolding-js",
|
|
3
|
-
"version": "5.2.0-next.2.
|
|
3
|
+
"version": "5.2.0-next.2.9",
|
|
4
4
|
"description": "Common scaffolding for Studio apps at Purdue",
|
|
5
5
|
"repository": "https://gitlab.com/purdue-informatics/studiokit/studiokit-scaffolding-js",
|
|
6
6
|
"license": "MIT",
|