studiokit-scaffolding-js 5.2.0-next.2.7 → 5.2.0-next.2.8

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.
@@ -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?: FetchErrorData | AddBusinessModel | undefined) => void;
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;
@@ -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: any) => void;
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
- if (errorAction === null || errorAction === void 0 ? void 0 : errorAction.errorData) {
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
- if (!(resultAction === null || resultAction === void 0 ? void 0 : resultAction.data)) {
97
- hook(null);
98
- return [2 /*return*/];
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
  });
@@ -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) { var _a; return ((_a = op.insert) === null || _a === void 0 ? void 0 : _a.image) && !checkedImages.find(function (image) { return image.src === op.insert.image.src; }); }).forEach(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 (op.insert.image.src && op.insert.image.src.startsWith('http')) {
37
- // Use the backend to check if the image is public
38
- // Due to CSP and other security measures, we can't check this in the frontend
39
- // uuidv5 is a deterministic uuid generator based on a namespace and a name
40
- // uuidv5.URL is the namespace for urls
41
- var hookId_1 = uuid_1.v5(op.insert.image.src, uuid_1.v5.URL);
42
- noStoreSaga_1.noStoreHooks.registerNoStoreActionHook(hookId_1, function (data) {
43
- noStoreSaga_1.noStoreHooks.unregisterNoStoreActionHook(hookId_1);
44
- // Backend fetches the URL. If there is no data returned or if the data returned has headers
45
- // but those headers are not of image type (i.e. it was a 302 that, when followed returned HTML
46
- // (I'm looking at you, Brightspace), then we update the image insert op to be nonPublic
47
- if (!data || (data['Content-Type'] && !data['Content-Type'][0].startsWith('image'))) {
48
- var contents = quill.getContents();
49
- var ops = contents.map(function (o) {
50
- var _a, _b;
51
- return ((_a = o.insert.image) === null || _a === void 0 ? void 0 : _a.src) && ((_b = op.insert.image) === null || _b === void 0 ? void 0 : _b.src) && o.insert.image.src === op.insert.image.src
52
- ? __assign(__assign({}, o), { insert: __assign(__assign({}, o.insert), { image: __assign(__assign({}, o.insert.image), { nonPublic: true }) }) }) : o;
53
- });
54
- var updatedContents = contents.diff(new Delta(ops));
55
- quill.updateContents(updatedContents);
56
- }
57
- });
58
- // This endpoint returns headers for a url. If the url is nonexistent, it will return null
59
- actionCreator_1.dispatchModelFetchRequest({
60
- modelName: 'urlChecker',
61
- guid: hookId_1,
62
- noStore: true,
63
- queryParams: {
64
- url: op.insert.image.src
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.7",
3
+ "version": "5.2.0-next.2.8",
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",