studiokit-scaffolding-js 7.0.12-next.1.4 → 7.0.12-next.2.2
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/ActionList.js +170 -34
- package/lib/components/AlertDialog.js +133 -9
- package/lib/components/AlertWithIcon.js +92 -25
- package/lib/components/ConnectedModal.js +43 -13
- package/lib/components/Dropdowns/GroupsDropdown.js +69 -44
- package/lib/components/Dropdowns/ManagedNavDropdown.js +100 -67
- package/lib/components/Dropdowns/UserDropdown.js +111 -21
- package/lib/components/Dropdowns/index.js +27 -9
- package/lib/components/EntityOwnerList.js +52 -18
- package/lib/components/Error.js +106 -11
- package/lib/components/ErrorBoundary.js +134 -35
- package/lib/components/ErrorMessage.js +44 -9
- package/lib/components/Forms/DateField.js +61 -42
- package/lib/components/Forms/TimeField.js +81 -42
- package/lib/components/Forms/index.js +27 -4
- package/lib/components/Groups/CreateEditCopySaveButtons.js +114 -11
- package/lib/components/Groups/ExternalGroups/Attach.js +212 -148
- package/lib/components/Groups/ExternalGroups/Table.js +181 -45
- package/lib/components/Groups/GroupCreateOrEditCommonProps.js +5 -1
- package/lib/components/Groups/RosterSyncInfo.js +147 -20
- package/lib/components/HOC/AccessibleAppComponent.js +98 -73
- package/lib/components/HOC/ActivityRequiredComponent.js +92 -49
- package/lib/components/HOC/AsyncComponent.js +54 -39
- package/lib/components/HOC/AuthenticatedComponent.js +58 -38
- package/lib/components/HOC/CollectionComponent.js +170 -110
- package/lib/components/HOC/CollectionFirstItemComponent.js +51 -38
- package/lib/components/HOC/CollectionItemComponent.js +168 -106
- package/lib/components/HOC/ConnectedModalComponent.js +109 -80
- package/lib/components/HOC/DataDependentComponent.js +29 -21
- package/lib/components/HOC/EntityComponent.js +71 -57
- package/lib/components/HOC/FullscreenModalComponent.js +163 -123
- package/lib/components/HOC/GroupActivityRequiredComponent.js +45 -31
- package/lib/components/HOC/GuidComponent.js +29 -22
- package/lib/components/HOC/ModelContextDependencyVerifyComponent.js +41 -31
- package/lib/components/HOC/ModelErrorRedirectComponent.js +51 -47
- package/lib/components/HOC/SearchPersistorComponent.js +240 -166
- package/lib/components/HOC/UnauthenticatedComponent.js +37 -25
- package/lib/components/HOC/UserComponent.js +12 -7
- package/lib/components/Icons/IconAlphaList.js +33 -5
- package/lib/components/Icons/IconExternalUser.js +33 -5
- package/lib/components/Icons/IconImpersonation.js +33 -5
- package/lib/components/Icons/IconStopImpersonating.js +33 -5
- package/lib/components/Icons/IconTable.js +35 -7
- package/lib/components/Icons/IconTableDeleteCol.js +33 -5
- package/lib/components/Icons/IconTableDeleteRow.js +33 -5
- package/lib/components/Icons/IconTableInsertCol.js +33 -5
- package/lib/components/Icons/IconTableInsertRow.js +33 -5
- package/lib/components/Impersonation/Button.js +77 -13
- package/lib/components/Impersonation/Link.js +77 -13
- package/lib/components/Impersonation/UserDetail.js +66 -9
- package/lib/components/Loading.js +26 -4
- package/lib/components/LockDownBrowser/Check.js +194 -49
- package/lib/components/LockDownBrowser/ExitButton.js +26 -9
- package/lib/components/LockDownBrowser/Launch.js +70 -62
- package/lib/components/Lti/Confirm.js +152 -11
- package/lib/components/Lti/CreateNonLtiGroupAlertDialog.js +170 -33
- package/lib/components/Lti/Launch.js +105 -24
- package/lib/components/Lti/LaunchGroup.js +85 -13
- package/lib/components/ManageTable.js +309 -87
- package/lib/components/ManageTableNoDataComponent.js +42 -4
- package/lib/components/NewVersionAlert.js +82 -46
- package/lib/components/NotFound.js +86 -10
- package/lib/components/Notifications.js +185 -126
- package/lib/components/PaginationNextButton.js +33 -6
- package/lib/components/PaginationPreviousButton.js +33 -6
- package/lib/components/Quill/CustomToolbar.js +432 -218
- package/lib/components/Quill/Formats/Image.js +73 -63
- package/lib/components/Quill/Formats/List.js +45 -45
- package/lib/components/Quill/Formats/Video.js +28 -24
- package/lib/components/Quill/ImageDropModule.js +147 -117
- package/lib/components/Quill/ImageWarning.js +47 -9
- package/lib/components/Quill/ImageWithAltTextModal.js +425 -86
- package/lib/components/Quill/Specs/CustomImageSpec.js +42 -34
- package/lib/components/Quill/Specs/CustomVideoSpec.js +34 -28
- package/lib/components/Quill/TableModule/Blots/BaseTableBlot.js +98 -98
- package/lib/components/Quill/TableModule/Blots/TableBlot.js +52 -47
- package/lib/components/Quill/TableModule/Blots/TableBodyBlot.js +53 -48
- package/lib/components/Quill/TableModule/Blots/TableCellBlot.js +224 -221
- package/lib/components/Quill/TableModule/Blots/TableContainer.js +80 -83
- package/lib/components/Quill/TableModule/Blots/TableRowBlot.js +75 -70
- package/lib/components/Quill/TableModule/constants.js +45 -41
- package/lib/components/Quill/TableModule/index.js +362 -301
- package/lib/components/Quill/TableModule/utils.js +42 -38
- package/lib/components/Quill/accessibilityFix.js +234 -232
- package/lib/components/Quill/index.js +34 -28
- package/lib/components/RefreshIndicator/Bordered.js +47 -6
- package/lib/components/RefreshIndicator/Inline.js +47 -8
- package/lib/components/RefreshIndicator/index.js +263 -59
- package/lib/components/SearchControls.js +216 -11
- package/lib/components/SentryRoute.js +11 -6
- package/lib/components/Tables/RoleFilter.js +69 -32
- package/lib/components/Tables/TextFilter.js +62 -13
- package/lib/components/UserRoles/Add.js +199 -96
- package/lib/components/UserRoles/Context.js +11 -7
- package/lib/components/UserRoles/RoleCell.js +181 -72
- package/lib/components/UserRoles/Select.js +157 -17
- package/lib/components/UserRoles/Table.js +221 -80
- package/lib/components/UserRoles/index.js +534 -384
- package/lib/config/eslint/index.js +32 -28
- package/lib/config/eslint/lib/order.js +26 -27
- package/lib/config/eslint/lib/prettier.js +20 -18
- package/lib/config/eslint/lib/typescript.js +93 -112
- package/lib/config/eslint/react.js +24 -14
- package/lib/constants/baseActivity.js +30 -26
- package/lib/constants/baseRole.js +14 -10
- package/lib/constants/configuration.js +33 -29
- package/lib/constants/externalProviderType.js +10 -6
- package/lib/constants/fetchErrorData.js +15 -11
- package/lib/constants/index.js +137 -14
- package/lib/constants/lockDownBrowser.js +28 -24
- package/lib/constants/mockData.js +382 -297
- package/lib/constants/modelStatus.js +15 -11
- package/lib/constants/notificationType.js +12 -8
- package/lib/constants/operatingSystem.js +12 -8
- package/lib/constants/shard.js +11 -7
- package/lib/constants/table.js +21 -21
- package/lib/constants/tier.js +12 -8
- package/lib/constants/userRole.js +15 -5
- package/lib/endpointMappings.js +197 -181
- package/lib/hooks/useCollection.js +82 -62
- package/lib/hooks/useCollectionConfiguration.js +228 -83
- package/lib/hooks/useCollectionItem.js +154 -54
- package/lib/hooks/useGuid.js +20 -8
- package/lib/hooks/usePrevious.js +19 -13
- package/lib/index.js +157 -25
- package/lib/redux/actionCreator.js +50 -28
- package/lib/redux/actions/AuthAction.js +44 -31
- package/lib/redux/actions/ModalAction.js +10 -6
- package/lib/redux/actions/ModelAction.js +77 -39
- package/lib/redux/actions/NotificationAction.js +10 -6
- package/lib/redux/actions/SearchAction.js +9 -5
- package/lib/redux/actions/index.js +60 -7
- package/lib/redux/configureReducers.js +60 -49
- package/lib/redux/configureStore.js +83 -87
- package/lib/redux/helpers.js +6 -2
- package/lib/redux/reducers/authReducer.js +50 -43
- package/lib/redux/reducers/index.js +41 -13
- package/lib/redux/reducers/modalsReducer.js +47 -29
- package/lib/redux/reducers/modelsReducer.js +178 -173
- package/lib/redux/reducers/notificationsReducer.js +24 -18
- package/lib/redux/reducers/searchReducer.js +25 -19
- package/lib/redux/sagas/appInsightsSaga.js +22 -18
- package/lib/redux/sagas/authSaga.js +253 -218
- package/lib/redux/sagas/caliperSaga.js +159 -143
- package/lib/redux/sagas/clockOffsetSaga.js +34 -31
- package/lib/redux/sagas/configurationSaga.js +11 -7
- package/lib/redux/sagas/downtimeApiErrorSaga.js +20 -17
- package/lib/redux/sagas/errorSaga.js +27 -21
- package/lib/redux/sagas/googleAnalyticsSaga.js +28 -24
- package/lib/redux/sagas/identityProviderSaga.js +22 -18
- package/lib/redux/sagas/initialDataLoadSaga.js +37 -28
- package/lib/redux/sagas/lockDownBrowserErrorSaga.js +29 -20
- package/lib/redux/sagas/modelFetchSaga.js +355 -322
- package/lib/redux/sagas/noStoreSaga.js +61 -48
- package/lib/redux/sagas/postLoginDataSaga.js +45 -34
- package/lib/redux/sagas/postLoginRedirectSaga.js +27 -27
- package/lib/redux/sagas/rootSaga.js +82 -57
- package/lib/redux/sagas/sentrySaga.js +29 -25
- package/lib/redux/sagas/userIdSaga.js +16 -12
- package/lib/services/codeProviderService.js +25 -19
- package/lib/services/dateService.js +12 -7
- package/lib/services/documentService.js +17 -12
- package/lib/services/fetchService.js +129 -112
- package/lib/services/persistenceService.js +33 -29
- package/lib/services/ticketProviderService.js +29 -23
- package/lib/services/tokenPersistenceService.js +12 -8
- package/lib/services/windowService.js +18 -14
- package/lib/startup.js +132 -114
- package/lib/types/AppConfiguration.js +5 -1
- package/lib/types/Artifact.js +11 -7
- package/lib/types/BaseReduxState.js +5 -1
- package/lib/types/Client.js +5 -1
- package/lib/types/Collection.js +5 -1
- package/lib/types/Configuration.js +5 -1
- package/lib/types/DeepLinkingResponseRequest.js +5 -1
- package/lib/types/DeletableModel.js +5 -1
- package/lib/types/Event.js +5 -1
- package/lib/types/ExternalGroup.js +5 -1
- package/lib/types/ExternalProvider.js +5 -1
- package/lib/types/ExternalTerm.js +5 -1
- package/lib/types/Group.js +5 -1
- package/lib/types/IdentityProvider.js +5 -1
- package/lib/types/LtiLaunch.js +5 -1
- package/lib/types/NameOnlyEntity.js +5 -1
- package/lib/types/Notification.js +5 -1
- package/lib/types/OptionalRecord.js +5 -1
- package/lib/types/OwnerSchedule.js +5 -1
- package/lib/types/PropertyOfType.js +5 -1
- package/lib/types/Quill.js +5 -1
- package/lib/types/RoleDescription.js +5 -1
- package/lib/types/Search.js +5 -1
- package/lib/types/SimpleLocation.js +5 -1
- package/lib/types/UniTime.js +5 -1
- package/lib/types/User.js +5 -1
- package/lib/types/UserRole.js +5 -1
- package/lib/types/auth/AuthState.js +5 -1
- package/lib/types/auth/CasV1LoginRequestBody.js +5 -1
- package/lib/types/auth/ClientCredentials.js +5 -1
- package/lib/types/auth/CodeProviderService.js +5 -1
- package/lib/types/auth/LocalLoginRequestBody.js +5 -1
- package/lib/types/auth/TicketProviderService.js +5 -1
- package/lib/types/auth/TokenPersistenceService.js +5 -1
- package/lib/types/auth/index.js +82 -9
- package/lib/types/externals.d.js +2 -0
- package/lib/types/index.js +313 -30
- package/lib/types/net/EndpointConfig.js +5 -1
- package/lib/types/net/EndpointMapping.js +5 -1
- package/lib/types/net/EndpointMappings.js +5 -1
- package/lib/types/net/ErrorHandler.js +5 -1
- package/lib/types/net/FetchConfig.js +5 -1
- package/lib/types/net/FetchErrorData.js +10 -6
- package/lib/types/net/FetchResult.js +5 -1
- package/lib/types/net/HTTPMethod.js +5 -1
- package/lib/types/net/HTTPStatusCode.js +16 -12
- package/lib/types/net/Metadata.js +5 -1
- package/lib/types/net/Model.js +5 -1
- package/lib/types/net/ModelCollection.js +5 -1
- package/lib/types/net/ModelsState.js +5 -1
- package/lib/types/net/OAuthToken.js +5 -1
- package/lib/types/net/OAuthTokenOrNull.js +5 -1
- package/lib/types/net/TokenAccessFunction.js +5 -1
- package/lib/types/net/index.js +181 -18
- package/lib/utils/baseActivity.js +133 -123
- package/lib/utils/baseRole.js +37 -33
- package/lib/utils/collection.js +425 -298
- package/lib/utils/cookies.js +22 -19
- package/lib/utils/date.js +303 -279
- package/lib/utils/dom.js +176 -165
- package/lib/utils/domainIdentifier.js +9 -5
- package/lib/utils/entityUserRole.js +6 -2
- package/lib/utils/error.js +17 -15
- package/lib/utils/events.js +40 -31
- package/lib/utils/externalGroup.js +22 -18
- package/lib/utils/externalProviders.js +8 -4
- package/lib/utils/externalTerms.js +10 -3
- package/lib/utils/fetch.js +179 -180
- package/lib/utils/group.js +18 -7
- package/lib/utils/groupDates.js +37 -33
- package/lib/utils/groupRoles.js +25 -21
- package/lib/utils/lockDownBrowser.js +15 -11
- package/lib/utils/logger.js +26 -22
- package/lib/utils/lti.js +9 -4
- package/lib/utils/model.js +36 -41
- package/lib/utils/number.js +21 -18
- package/lib/utils/promise.js +28 -21
- package/lib/utils/quill.js +65 -62
- package/lib/utils/route.js +58 -55
- package/lib/utils/search.js +76 -80
- package/lib/utils/shard.js +37 -37
- package/lib/utils/sort.js +50 -42
- package/lib/utils/string.js +13 -8
- package/lib/utils/table.js +38 -33
- package/lib/utils/timezone.js +10 -6
- package/lib/utils/url.js +142 -142
- package/lib/utils/user.js +58 -55
- package/lib/utils/userAgent.js +10 -10
- package/lib/utils/userRole.js +57 -49
- package/package.json +17 -3
|
@@ -1,380 +1,413 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
exports
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.TRY_LIMIT = void 0;
|
|
8
|
+
exports.default = modelFetchSaga;
|
|
9
|
+
exports.matchesTerminationAction = exports.defaultTokenAccessFunction = exports.defaultErrorHandler = void 0;
|
|
5
10
|
exports.modelFetch = modelFetch;
|
|
6
11
|
exports.modelFetchLoop = modelFetchLoop;
|
|
7
12
|
exports.modelFetchRecurring = modelFetchRecurring;
|
|
8
|
-
exports.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
exports.setEndpointMappings = setEndpointMappings;
|
|
14
|
+
exports.takeMatchesTerminationAction = void 0;
|
|
15
|
+
var _lodash = require("lodash");
|
|
16
|
+
var _momentTimezone = _interopRequireDefault(require("moment-timezone"));
|
|
17
|
+
var _effects = require("redux-saga/effects");
|
|
18
|
+
var _fetchErrorData = require("../../constants/fetchErrorData");
|
|
19
|
+
var _dateService = require("../../services/dateService");
|
|
20
|
+
var _fetchService = require("../../services/fetchService");
|
|
21
|
+
var _windowService = require("../../services/windowService");
|
|
22
|
+
var _types = require("../../types");
|
|
23
|
+
var _error = require("../../utils/error");
|
|
24
|
+
var _fetch = require("../../utils/fetch");
|
|
25
|
+
var _logger = require("../../utils/logger");
|
|
26
|
+
var _actions = require("../actions");
|
|
22
27
|
/** The total number of tries for `fetchData`, including the initial request. */
|
|
23
|
-
exports.TRY_LIMIT = 5;
|
|
28
|
+
const TRY_LIMIT = exports.TRY_LIMIT = 5;
|
|
29
|
+
|
|
24
30
|
//#region Helpers
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
|
|
32
|
+
const matchesTerminationAction = (incomingAction, dataRequestAction) => incomingAction.type === _actions.PERIODIC_MODEL_FETCH_TERMINATION_ACTION_TYPE.TERMINATE && incomingAction.taskId === dataRequestAction.taskId;
|
|
27
33
|
exports.matchesTerminationAction = matchesTerminationAction;
|
|
28
|
-
const takeMatchesTerminationAction =
|
|
29
|
-
|
|
34
|
+
const takeMatchesTerminationAction = dataRequestAction => incomingAction => matchesTerminationAction(incomingAction, dataRequestAction);
|
|
35
|
+
|
|
30
36
|
/* istanbul ignore next */
|
|
31
37
|
/* eslint-disable-next-line require-yield */
|
|
38
|
+
exports.takeMatchesTerminationAction = takeMatchesTerminationAction;
|
|
32
39
|
const defaultTokenAccessFunction = function* () {
|
|
33
|
-
|
|
40
|
+
return null;
|
|
34
41
|
};
|
|
35
|
-
|
|
42
|
+
|
|
36
43
|
/* istanbul ignore next */
|
|
44
|
+
exports.defaultTokenAccessFunction = defaultTokenAccessFunction;
|
|
37
45
|
const defaultErrorHandler = () => {
|
|
38
|
-
|
|
46
|
+
return;
|
|
39
47
|
};
|
|
40
48
|
exports.defaultErrorHandler = defaultErrorHandler;
|
|
41
|
-
const getChildCollectionKeys =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return out;
|
|
45
|
-
}
|
|
46
|
-
const em = value;
|
|
47
|
-
if (!!em._config && !!em._config.isCollection) {
|
|
48
|
-
out.push(key);
|
|
49
|
-
}
|
|
49
|
+
const getChildCollectionKeys = endpointMapping => Object.keys(endpointMapping).reduce((out, key) => {
|
|
50
|
+
const value = endpointMapping[key];
|
|
51
|
+
if (!value || !Object.prototype.hasOwnProperty.call(value, '_config')) {
|
|
50
52
|
return out;
|
|
53
|
+
}
|
|
54
|
+
const em = value;
|
|
55
|
+
if (!!em._config && !!em._config.isCollection) {
|
|
56
|
+
out.push(key);
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
51
59
|
}, []);
|
|
52
60
|
const convertCollections = (endpointMapping, prepareFetchResult, data) => {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
const {
|
|
62
|
+
endpointConfig,
|
|
63
|
+
fetchConfig,
|
|
64
|
+
isCollectionItemCreate,
|
|
65
|
+
isCollectionItemFetch
|
|
66
|
+
} = prepareFetchResult;
|
|
67
|
+
const childCollectionKeys = getChildCollectionKeys(endpointMapping);
|
|
68
|
+
const hasChildCollections = childCollectionKeys.length > 0;
|
|
69
|
+
// no collections, return as-is
|
|
70
|
+
if (!endpointConfig.isCollection && !hasChildCollections) {
|
|
71
|
+
return data;
|
|
72
|
+
}
|
|
73
|
+
// delete returns no data
|
|
74
|
+
if (fetchConfig.method === 'DELETE') {
|
|
75
|
+
return {};
|
|
76
|
+
}
|
|
77
|
+
// data is a collection item, convert any child collections in item response
|
|
78
|
+
if (isCollectionItemFetch || isCollectionItemCreate) {
|
|
79
|
+
return convertDataCollectionItem(data, childCollectionKeys, endpointMapping);
|
|
80
|
+
}
|
|
81
|
+
// data is a collection, convert collection response, and any child collections in each item
|
|
82
|
+
return endpointConfig.isCollection ? convertDataToCollection(data, childCollectionKeys, endpointMapping) : convertDataCollectionItem(data, childCollectionKeys, endpointMapping);
|
|
72
83
|
};
|
|
73
84
|
const convertDataCollectionItem = (data, collectionKeys, endpointMapping) => {
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
if ((0, _lodash.isNil)(data)) {
|
|
86
|
+
return data;
|
|
87
|
+
}
|
|
88
|
+
return Object.keys(data).reduce((out, key) => {
|
|
89
|
+
let value = data[key];
|
|
90
|
+
if (collectionKeys.includes(key)) {
|
|
91
|
+
const childEndpointMapping = endpointMapping[key];
|
|
92
|
+
const childCollectionKeys = getChildCollectionKeys(childEndpointMapping);
|
|
93
|
+
value = convertDataToCollection(value, childCollectionKeys, childEndpointMapping);
|
|
76
94
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const childEndpointMapping = endpointMapping[key];
|
|
81
|
-
const childCollectionKeys = getChildCollectionKeys(childEndpointMapping);
|
|
82
|
-
value = convertDataToCollection(value, childCollectionKeys, childEndpointMapping);
|
|
83
|
-
}
|
|
84
|
-
out[key] = value;
|
|
85
|
-
return out;
|
|
86
|
-
}, {});
|
|
95
|
+
out[key] = value;
|
|
96
|
+
return out;
|
|
97
|
+
}, {});
|
|
87
98
|
};
|
|
88
99
|
const convertDataToCollection = (data, collectionKeys, endpointMapping) => {
|
|
89
|
-
|
|
90
|
-
|
|
100
|
+
if ((0, _lodash.isNil)(data)) {
|
|
101
|
+
return data;
|
|
102
|
+
}
|
|
103
|
+
return Object.keys(data).reduce((out, key) => {
|
|
104
|
+
const value = convertDataCollectionItem(data[key], collectionKeys, endpointMapping);
|
|
105
|
+
/* istanbul ignore else */
|
|
106
|
+
if (value && value.id) {
|
|
107
|
+
out[value.id] = value;
|
|
91
108
|
}
|
|
92
|
-
return
|
|
93
|
-
|
|
94
|
-
/* istanbul ignore else */
|
|
95
|
-
if (value && value.id) {
|
|
96
|
-
out[value.id] = value;
|
|
97
|
-
}
|
|
98
|
-
return out;
|
|
99
|
-
}, {});
|
|
109
|
+
return out;
|
|
110
|
+
}, {});
|
|
100
111
|
};
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
*
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Using the `lastActivityDate` set in local storage by "studiokit-caliper-js",
|
|
115
|
+
* determine if the user has been idle, e.g. has had no activity in the last 15 minutes.
|
|
104
116
|
*/
|
|
105
117
|
function isUserIdle() {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
118
|
+
const lastActivityDateString = (0, _dateService.getLastActivityDate)();
|
|
119
|
+
if (!lastActivityDateString) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
const lastActivityDateUtc = _momentTimezone.default.utc(lastActivityDateString);
|
|
123
|
+
const nowUtc = _momentTimezone.default.utc();
|
|
124
|
+
const idleMinutesThreshold = 1000 * 60 * 15; // 15 minutes
|
|
125
|
+
|
|
126
|
+
return nowUtc.diff(lastActivityDateUtc) >= idleMinutesThreshold;
|
|
114
127
|
}
|
|
128
|
+
|
|
115
129
|
//#endregion Helpers
|
|
130
|
+
|
|
116
131
|
//#region Local Variables
|
|
132
|
+
|
|
117
133
|
let endpointMappings;
|
|
118
134
|
let tokenAccessFunction;
|
|
119
135
|
let errorHandler;
|
|
120
136
|
let logger;
|
|
137
|
+
|
|
121
138
|
//#endregion Local Variables
|
|
139
|
+
|
|
122
140
|
/** Set the shared EndpointMappings variable, exposed for testability */
|
|
123
141
|
function setEndpointMappings(endpointMappingsParam) {
|
|
124
|
-
|
|
142
|
+
endpointMappings = endpointMappingsParam;
|
|
125
143
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Construct a request based on the provided dataRequestAction, make a request with a configurable retry,
|
|
147
|
+
* and handle errors, logging and dispatching all steps.
|
|
148
|
+
*
|
|
149
|
+
* @param modelFetchRequestAction A model fetch request action with the request configuration
|
|
131
150
|
*/
|
|
132
151
|
function* modelFetch(modelFetchRequestAction) {
|
|
133
|
-
|
|
152
|
+
let prepareFetchResult = undefined;
|
|
153
|
+
try {
|
|
154
|
+
prepareFetchResult = (0, _fetch.prepareFetch)(modelFetchRequestAction, endpointMappings);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
// Note: `prepareFetch` already ensures that `error` is an actual `Error`
|
|
157
|
+
logger.error(error);
|
|
158
|
+
errorHandler(error, modelFetchRequestAction);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// get prepared variables
|
|
163
|
+
const {
|
|
164
|
+
modelPath,
|
|
165
|
+
fetchConfig,
|
|
166
|
+
endpointMapping,
|
|
167
|
+
isCollectionItemCreate,
|
|
168
|
+
isCollectionItemDelete
|
|
169
|
+
} = prepareFetchResult;
|
|
170
|
+
|
|
171
|
+
// Configure retry
|
|
172
|
+
const tryLimit = modelFetchRequestAction.noRetry ? 1 : TRY_LIMIT;
|
|
173
|
+
let tryCount = 0;
|
|
174
|
+
let didFail = false;
|
|
175
|
+
let lastFetchResult;
|
|
176
|
+
let lastError;
|
|
177
|
+
let lastErrorData;
|
|
178
|
+
const fetchResultUndefinedErrorMessage = "'fetchResult' is undefined";
|
|
179
|
+
const fetchResultNotOkErrorMessage = "'fetchResult.ok' is false";
|
|
180
|
+
|
|
181
|
+
// Run retry loop
|
|
182
|
+
do {
|
|
183
|
+
didFail = false;
|
|
184
|
+
tryCount++;
|
|
185
|
+
|
|
186
|
+
// dispatch that the fetch request started, which updates `_metadata` and `guid`, if provided, at the target redux `modelPath`
|
|
187
|
+
yield (0, _effects.put)({
|
|
188
|
+
type: modelFetchRequestAction.noStore ? _actions.MODEL_FETCH_START_ACTION_TYPE.TRANSIENT_FETCH_START : _actions.MODEL_FETCH_START_ACTION_TYPE.FETCH_START,
|
|
189
|
+
modelPath,
|
|
190
|
+
guid: modelFetchRequestAction.guid
|
|
191
|
+
});
|
|
192
|
+
let fetchResult = undefined;
|
|
134
193
|
try {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
errorHandler(error, modelFetchRequestAction);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
// get prepared variables
|
|
144
|
-
const { modelPath, fetchConfig, endpointMapping, isCollectionItemCreate, isCollectionItemDelete } = prepareFetchResult;
|
|
145
|
-
// Configure retry
|
|
146
|
-
const tryLimit = modelFetchRequestAction.noRetry ? 1 : exports.TRY_LIMIT;
|
|
147
|
-
let tryCount = 0;
|
|
148
|
-
let didFail = false;
|
|
149
|
-
let lastFetchResult;
|
|
150
|
-
let lastError;
|
|
151
|
-
let lastErrorData;
|
|
152
|
-
const fetchResultUndefinedErrorMessage = "'fetchResult' is undefined";
|
|
153
|
-
const fetchResultNotOkErrorMessage = "'fetchResult.ok' is false";
|
|
154
|
-
// Run retry loop
|
|
155
|
-
do {
|
|
156
|
-
didFail = false;
|
|
157
|
-
tryCount++;
|
|
158
|
-
// dispatch that the fetch request started, which updates `_metadata` and `guid`, if provided, at the target redux `modelPath`
|
|
159
|
-
yield (0, effects_1.put)({
|
|
160
|
-
type: modelFetchRequestAction.noStore
|
|
161
|
-
? actions_1.MODEL_FETCH_START_ACTION_TYPE.TRANSIENT_FETCH_START
|
|
162
|
-
: actions_1.MODEL_FETCH_START_ACTION_TYPE.FETCH_START,
|
|
163
|
-
modelPath,
|
|
164
|
-
guid: modelFetchRequestAction.guid
|
|
194
|
+
// get the OAuth Token, if any, and call `fetch`
|
|
195
|
+
const oauthToken = yield (0, _effects.call)(tokenAccessFunction, modelFetchRequestAction.modelName);
|
|
196
|
+
if (oauthToken?.access_token) {
|
|
197
|
+
fetchConfig.headers = (0, _lodash.merge)({}, fetchConfig.headers, {
|
|
198
|
+
Authorization: `Bearer ${oauthToken.access_token}`
|
|
165
199
|
});
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
type: actions_1.MODEL_REMOVE_KEY_ACTION_TYPE,
|
|
208
|
-
modelPath: modelPath
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
// DELETE collection item
|
|
212
|
-
else if (isCollectionItemDelete) {
|
|
213
|
-
yield (0, effects_1.put)({
|
|
214
|
-
type: actions_1.MODEL_REMOVE_KEY_ACTION_TYPE,
|
|
215
|
-
modelPath: modelPath
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
yield (0, effects_1.put)({
|
|
220
|
-
type: resultActionType,
|
|
221
|
-
modelPath: modelPath,
|
|
222
|
-
guid: modelFetchRequestAction.guid,
|
|
223
|
-
replaceValue: modelFetchRequestAction.replaceValue,
|
|
224
|
-
data
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
catch (caughtError) {
|
|
229
|
-
// construct `Error` using `caughtError`
|
|
230
|
-
const baseErrorMessage = `'modelFetch' failed for '${modelPath}'`;
|
|
231
|
-
const error = (0, error_1.constructErrorFromCaughtError)(caughtError, 'ModelFetchError', baseErrorMessage);
|
|
232
|
-
// create a default `errorData` object
|
|
233
|
-
const isNetworkOnline = windowService_1.windowService.getIsOnline();
|
|
234
|
-
let errorData = isNetworkOnline ? fetchErrorData_1.unknownErrorData : fetchErrorData_1.networkOfflineErrorData;
|
|
235
|
-
// update `errorData` from the `fetchResult.data`
|
|
236
|
-
// for when the fetch got a result, but it was an unsuccessful status
|
|
237
|
-
if ((fetchResult === null || fetchResult === void 0 ? void 0 : fetchResult.data) && (0, fetch_1.isFetchErrorData)(fetchResult.data)) {
|
|
238
|
-
errorData = fetchResult.data;
|
|
239
|
-
// update the error's message to include info from the fetch result
|
|
240
|
-
error.message = `${baseErrorMessage}.\n${fetchConfig.method || 'GET'} ${fetchConfig.path} failed.\n${errorData.title} (${errorData.status})`;
|
|
241
|
-
if (errorData.detail) {
|
|
242
|
-
error.message += `: ${errorData.detail}`;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
// track `errorData` and `error` for after the while-loop
|
|
246
|
-
lastErrorData = errorData;
|
|
247
|
-
lastError = error;
|
|
248
|
-
// dispatch that this try failed
|
|
249
|
-
yield (0, effects_1.put)({
|
|
250
|
-
type: actions_1.MODEL_FETCH_ERROR_ACTION_TYPE.TRY_FETCH_FAILED,
|
|
251
|
-
modelPath: modelPath,
|
|
252
|
-
guid: modelFetchRequestAction.guid,
|
|
253
|
-
errorData
|
|
254
|
-
});
|
|
255
|
-
// log to the console
|
|
256
|
-
logger.error(error);
|
|
257
|
-
didFail = true;
|
|
258
|
-
// Do not retry if the response is between 400-500 (except 408: request timeout)
|
|
259
|
-
if (errorData.status >= types_1.HTTP_STATUS_CODE.BAD_REQUEST &&
|
|
260
|
-
errorData.status < types_1.HTTP_STATUS_CODE.INTERNAL_SERVER_ERROR &&
|
|
261
|
-
errorData.status !== types_1.HTTP_STATUS_CODE.REQUEST_TIMEOUT) {
|
|
262
|
-
tryCount = tryLimit;
|
|
263
|
-
}
|
|
264
|
-
// if there are tries remaining, perform an exponential backoff
|
|
265
|
-
if (tryCount < tryLimit) {
|
|
266
|
-
yield (0, effects_1.delay)(Math.pow(2, (tryCount - 1)) * 100); // 100, 200, 400, 800
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
} while (tryCount < tryLimit && didFail);
|
|
270
|
-
// if fetch failed completely after all retries
|
|
271
|
-
if (tryCount === tryLimit && didFail) {
|
|
272
|
-
// these will always exist
|
|
273
|
-
const errorData = lastErrorData;
|
|
274
|
-
const error = lastError;
|
|
275
|
-
// dispatch that the fetch failed, which updates `_metadata` (and `guid`, if provided) at the target redux `modelPath`
|
|
276
|
-
yield (0, effects_1.put)({
|
|
277
|
-
type: modelFetchRequestAction.noStore
|
|
278
|
-
? actions_1.MODEL_FETCH_ERROR_ACTION_TYPE.TRANSIENT_FETCH_FAILED
|
|
279
|
-
: actions_1.MODEL_FETCH_ERROR_ACTION_TYPE.FETCH_FAILED,
|
|
280
|
-
modelPath: modelPath,
|
|
281
|
-
guid: modelFetchRequestAction.guid,
|
|
282
|
-
errorData
|
|
200
|
+
}
|
|
201
|
+
fetchResult = yield (0, _effects.call)(_fetchService.sendFetch, fetchConfig);
|
|
202
|
+
// store `fetchResult` for reference after the do-while loop
|
|
203
|
+
lastFetchResult = fetchResult;
|
|
204
|
+
|
|
205
|
+
// throw on unsuccessful result to short-circuit and trigger catch+retry
|
|
206
|
+
if (!fetchResult) throw new Error(fetchResultUndefinedErrorMessage);
|
|
207
|
+
if (!fetchResult.ok) throw new Error(fetchResultNotOkErrorMessage);
|
|
208
|
+
|
|
209
|
+
// handle success
|
|
210
|
+
const resultActionType = modelFetchRequestAction.noStore ? _actions.MODEL_FETCH_RESULT_ACTION_TYPE.TRANSIENT_FETCH_RESULT_RECEIVED : _actions.MODEL_FETCH_RESULT_ACTION_TYPE.FETCH_RESULT_RECEIVED;
|
|
211
|
+
const data = convertCollections(endpointMapping, prepareFetchResult, fetchResult.data);
|
|
212
|
+
// attach guid to result data if it is an object
|
|
213
|
+
if (modelFetchRequestAction.guid && (0, _lodash.isPlainObject)(data)) {
|
|
214
|
+
data.guid = modelFetchRequestAction.guid;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// POST new collection item
|
|
218
|
+
if (isCollectionItemCreate) {
|
|
219
|
+
const modelPathLevels = modelPath.split('.');
|
|
220
|
+
// remove guid
|
|
221
|
+
modelPathLevels.pop();
|
|
222
|
+
// add by new result's id
|
|
223
|
+
yield (0, _effects.put)({
|
|
224
|
+
type: resultActionType,
|
|
225
|
+
modelPath: `${modelPathLevels.join('.')}.${data.id}`,
|
|
226
|
+
guid: modelFetchRequestAction.guid,
|
|
227
|
+
replaceValue: modelFetchRequestAction.replaceValue,
|
|
228
|
+
data
|
|
229
|
+
});
|
|
230
|
+
// remove temp item under guid key
|
|
231
|
+
yield (0, _effects.put)({
|
|
232
|
+
type: _actions.MODEL_REMOVE_KEY_ACTION_TYPE,
|
|
233
|
+
modelPath: modelPath
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
// DELETE collection item
|
|
237
|
+
else if (isCollectionItemDelete) {
|
|
238
|
+
yield (0, _effects.put)({
|
|
239
|
+
type: _actions.MODEL_REMOVE_KEY_ACTION_TYPE,
|
|
240
|
+
modelPath: modelPath
|
|
283
241
|
});
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
242
|
+
} else {
|
|
243
|
+
yield (0, _effects.put)({
|
|
244
|
+
type: resultActionType,
|
|
245
|
+
modelPath: modelPath,
|
|
246
|
+
guid: modelFetchRequestAction.guid,
|
|
247
|
+
replaceValue: modelFetchRequestAction.replaceValue,
|
|
248
|
+
data
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
} catch (caughtError) {
|
|
252
|
+
// construct `Error` using `caughtError`
|
|
253
|
+
const baseErrorMessage = `'modelFetch' failed for '${modelPath}'`;
|
|
254
|
+
const error = (0, _error.constructErrorFromCaughtError)(caughtError, 'ModelFetchError', baseErrorMessage);
|
|
255
|
+
// create a default `errorData` object
|
|
256
|
+
const isNetworkOnline = _windowService.windowService.getIsOnline();
|
|
257
|
+
let errorData = isNetworkOnline ? _fetchErrorData.unknownErrorData : _fetchErrorData.networkOfflineErrorData;
|
|
258
|
+
// update `errorData` from the `fetchResult.data`
|
|
259
|
+
// for when the fetch got a result, but it was an unsuccessful status
|
|
260
|
+
if (fetchResult?.data && (0, _fetch.isFetchErrorData)(fetchResult.data)) {
|
|
261
|
+
errorData = fetchResult.data;
|
|
262
|
+
// update the error's message to include info from the fetch result
|
|
263
|
+
error.message = `${baseErrorMessage}.\n${fetchConfig.method || 'GET'} ${fetchConfig.path} failed.\n${errorData.title} (${errorData.status})`;
|
|
264
|
+
if (errorData.detail) {
|
|
265
|
+
error.message += `: ${errorData.detail}`;
|
|
287
266
|
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// track `errorData` and `error` for after the while-loop
|
|
270
|
+
lastErrorData = errorData;
|
|
271
|
+
lastError = error;
|
|
272
|
+
|
|
273
|
+
// dispatch that this try failed
|
|
274
|
+
yield (0, _effects.put)({
|
|
275
|
+
type: _actions.MODEL_FETCH_ERROR_ACTION_TYPE.TRY_FETCH_FAILED,
|
|
276
|
+
modelPath: modelPath,
|
|
277
|
+
guid: modelFetchRequestAction.guid,
|
|
278
|
+
errorData
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// log to the console
|
|
282
|
+
logger.error(error);
|
|
283
|
+
didFail = true;
|
|
284
|
+
|
|
285
|
+
// Do not retry if the response is between 400-500 (except 408: request timeout)
|
|
286
|
+
if (errorData.status >= _types.HTTP_STATUS_CODE.BAD_REQUEST && errorData.status < _types.HTTP_STATUS_CODE.INTERNAL_SERVER_ERROR && errorData.status !== _types.HTTP_STATUS_CODE.REQUEST_TIMEOUT) {
|
|
287
|
+
tryCount = tryLimit;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// if there are tries remaining, perform an exponential backoff
|
|
291
|
+
if (tryCount < tryLimit) {
|
|
292
|
+
yield (0, _effects.delay)(2 ** (tryCount - 1) * 100); // 100, 200, 400, 800
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
} while (tryCount < tryLimit && didFail);
|
|
296
|
+
|
|
297
|
+
// if fetch failed completely after all retries
|
|
298
|
+
if (tryCount === tryLimit && didFail) {
|
|
299
|
+
// these will always exist
|
|
300
|
+
const errorData = lastErrorData;
|
|
301
|
+
const error = lastError;
|
|
302
|
+
|
|
303
|
+
// dispatch that the fetch failed, which updates `_metadata` (and `guid`, if provided) at the target redux `modelPath`
|
|
304
|
+
yield (0, _effects.put)({
|
|
305
|
+
type: modelFetchRequestAction.noStore ? _actions.MODEL_FETCH_ERROR_ACTION_TYPE.TRANSIENT_FETCH_FAILED : _actions.MODEL_FETCH_ERROR_ACTION_TYPE.FETCH_FAILED,
|
|
306
|
+
modelPath: modelPath,
|
|
307
|
+
guid: modelFetchRequestAction.guid,
|
|
308
|
+
errorData
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Send error to error handler, except for 401s
|
|
312
|
+
if (errorData.status !== _types.HTTP_STATUS_CODE.UNAUTHORIZED) {
|
|
313
|
+
errorHandler(error, modelFetchRequestAction, fetchConfig, lastFetchResult, errorData);
|
|
288
314
|
}
|
|
315
|
+
}
|
|
289
316
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
*
|
|
293
|
-
*
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The loop saga that makes the request every {action.period} milliseconds until cancelled
|
|
320
|
+
*
|
|
321
|
+
* @param action An action with the request configuration
|
|
294
322
|
*/
|
|
295
323
|
function* modelFetchLoop(action) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
taskId: action.taskId
|
|
319
|
-
});
|
|
320
|
-
}
|
|
324
|
+
try {
|
|
325
|
+
let hasFetched = false;
|
|
326
|
+
do {
|
|
327
|
+
// call the fetch if this is the first loop iteration,
|
|
328
|
+
// or if the user is not idle
|
|
329
|
+
if (!hasFetched || !isUserIdle()) {
|
|
330
|
+
yield (0, _effects.call)(modelFetch, action);
|
|
331
|
+
hasFetched = true;
|
|
332
|
+
}
|
|
333
|
+
yield (0, _effects.delay)(action.period);
|
|
334
|
+
} while (true);
|
|
335
|
+
} catch (caughtError) {
|
|
336
|
+
const error = (0, _error.constructErrorFromCaughtError)(caughtError, 'ModelFetchLoopError', 'modelFetchLoop failed');
|
|
337
|
+
logger.error(error);
|
|
338
|
+
// catch and log any unexpected errors not caught inside `fetchData`
|
|
339
|
+
errorHandler(error, action);
|
|
340
|
+
} finally {
|
|
341
|
+
if (yield (0, _effects.cancelled)()) {
|
|
342
|
+
yield (0, _effects.put)({
|
|
343
|
+
type: _actions.PERIODIC_MODEL_FETCH_TERMINATION_ACTION_TYPE.SUCCEEDED,
|
|
344
|
+
taskId: action.taskId
|
|
345
|
+
});
|
|
321
346
|
}
|
|
347
|
+
}
|
|
322
348
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Call the fetchData saga every {action.period} milliseconds. This saga requires the 'period' and 'taskId' properties
|
|
352
|
+
* on the action parameter.
|
|
353
|
+
*
|
|
354
|
+
* @param action An action with the request configuration
|
|
328
355
|
*/
|
|
329
356
|
function* modelFetchRecurring(action) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
357
|
+
if (!action.period) {
|
|
358
|
+
throw new Error("'period' is required");
|
|
359
|
+
}
|
|
360
|
+
if (!action.taskId) {
|
|
361
|
+
throw new Error("'taskId' is required");
|
|
362
|
+
}
|
|
363
|
+
const bgSyncTask = yield (0, _effects.fork)(modelFetchLoop, action);
|
|
364
|
+
yield (0, _effects.take)(takeMatchesTerminationAction(action));
|
|
365
|
+
yield (0, _effects.cancel)(bgSyncTask);
|
|
339
366
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
* `
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
* @
|
|
368
|
-
* @param
|
|
369
|
-
* @param
|
|
370
|
-
* @param
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* The main saga for fetching models. Must be initialized with an EndpointMappings object that can be fetched
|
|
370
|
+
* and an API root to prepend to any partial URLs specified in the EndpointMappings object. A logger should normally be provided
|
|
371
|
+
* as well.
|
|
372
|
+
*
|
|
373
|
+
* `EndpointMappings` object require a form as follows (with optional nested models):
|
|
374
|
+
* ```
|
|
375
|
+
* {
|
|
376
|
+
* fryModel: {
|
|
377
|
+
* path: '/api/Foo'
|
|
378
|
+
* },
|
|
379
|
+
* groupOfModels: {
|
|
380
|
+
* leelaModel: {
|
|
381
|
+
* path: '/api/Bar'
|
|
382
|
+
* },
|
|
383
|
+
* benderModel: {
|
|
384
|
+
* path: '/api/Baz'
|
|
385
|
+
* }
|
|
386
|
+
* }
|
|
387
|
+
* }
|
|
388
|
+
* ```
|
|
389
|
+
* `EndpointMapping` objects are referenced in the actions.DATA_REQUESTED action by path, i.e.
|
|
390
|
+
* `{ type: actions.DATA_REQUESTED, { modelName: 'fryModel' } }`
|
|
391
|
+
* -- or --
|
|
392
|
+
* `{ type: actions.DATA_REQUESTED, { modelName: 'groupOfModels.leelaModel' } }`
|
|
393
|
+
*
|
|
394
|
+
* @export
|
|
395
|
+
* @param endpointMappingsParam A mapping of API endpoints available in the application
|
|
396
|
+
* @param apiRootParam A url to which partial URLs are appended (i.e.) 'https://myapp.com'
|
|
397
|
+
* @param tokenAccessFunctionParam function that returns an optional OAuth token
|
|
398
|
+
* @param errorHandlerParam A function that is called when any fetch exceptions occur
|
|
371
399
|
*/
|
|
372
|
-
function
|
|
373
|
-
|
|
400
|
+
function modelFetchSaga(endpointMappingsParam, apiRootParam) {
|
|
401
|
+
let tokenAccessFunctionParam = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : defaultTokenAccessFunction;
|
|
402
|
+
let errorHandlerParam = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : defaultErrorHandler;
|
|
403
|
+
return function* () {
|
|
404
|
+
(0, _fetchService.setApiRoot)(apiRootParam);
|
|
374
405
|
setEndpointMappings(endpointMappingsParam);
|
|
375
406
|
errorHandler = errorHandlerParam;
|
|
376
407
|
tokenAccessFunction = tokenAccessFunctionParam;
|
|
377
|
-
logger = (0,
|
|
378
|
-
yield (0,
|
|
379
|
-
yield (0,
|
|
408
|
+
logger = (0, _logger.getLogger)();
|
|
409
|
+
yield (0, _effects.takeEvery)(_actions.MODEL_FETCH_REQUEST_ACTION_TYPE.FETCH_REQUEST, modelFetch);
|
|
410
|
+
yield (0, _effects.takeEvery)(_actions.MODEL_FETCH_REQUEST_ACTION_TYPE.PERIODIC_FETCH_REQUEST, modelFetchRecurring);
|
|
411
|
+
}();
|
|
380
412
|
}
|
|
413
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbG9kYXNoIiwicmVxdWlyZSIsIl9tb21lbnRUaW1lem9uZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJfZWZmZWN0cyIsIl9mZXRjaEVycm9yRGF0YSIsIl9kYXRlU2VydmljZSIsIl9mZXRjaFNlcnZpY2UiLCJfd2luZG93U2VydmljZSIsIl90eXBlcyIsIl9lcnJvciIsIl9mZXRjaCIsIl9sb2dnZXIiLCJfYWN0aW9ucyIsIlRSWV9MSU1JVCIsImV4cG9ydHMiLCJtYXRjaGVzVGVybWluYXRpb25BY3Rpb24iLCJpbmNvbWluZ0FjdGlvbiIsImRhdGFSZXF1ZXN0QWN0aW9uIiwidHlwZSIsIlBFUklPRElDX01PREVMX0ZFVENIX1RFUk1JTkFUSU9OX0FDVElPTl9UWVBFIiwiVEVSTUlOQVRFIiwidGFza0lkIiwidGFrZU1hdGNoZXNUZXJtaW5hdGlvbkFjdGlvbiIsImRlZmF1bHRUb2tlbkFjY2Vzc0Z1bmN0aW9uIiwiZGVmYXVsdEVycm9ySGFuZGxlciIsImdldENoaWxkQ29sbGVjdGlvbktleXMiLCJlbmRwb2ludE1hcHBpbmciLCJPYmplY3QiLCJrZXlzIiwicmVkdWNlIiwib3V0Iiwia2V5IiwidmFsdWUiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJlbSIsIl9jb25maWciLCJpc0NvbGxlY3Rpb24iLCJwdXNoIiwiY29udmVydENvbGxlY3Rpb25zIiwicHJlcGFyZUZldGNoUmVzdWx0IiwiZGF0YSIsImVuZHBvaW50Q29uZmlnIiwiZmV0Y2hDb25maWciLCJpc0NvbGxlY3Rpb25JdGVtQ3JlYXRlIiwiaXNDb2xsZWN0aW9uSXRlbUZldGNoIiwiY2hpbGRDb2xsZWN0aW9uS2V5cyIsImhhc0NoaWxkQ29sbGVjdGlvbnMiLCJsZW5ndGgiLCJtZXRob2QiLCJjb252ZXJ0RGF0YUNvbGxlY3Rpb25JdGVtIiwiY29udmVydERhdGFUb0NvbGxlY3Rpb24iLCJjb2xsZWN0aW9uS2V5cyIsImlzTmlsIiwiaW5jbHVkZXMiLCJjaGlsZEVuZHBvaW50TWFwcGluZyIsImlkIiwiaXNVc2VySWRsZSIsImxhc3RBY3Rpdml0eURhdGVTdHJpbmciLCJnZXRMYXN0QWN0aXZpdHlEYXRlIiwibGFzdEFjdGl2aXR5RGF0ZVV0YyIsIm1vbWVudCIsInV0YyIsIm5vd1V0YyIsImlkbGVNaW51dGVzVGhyZXNob2xkIiwiZGlmZiIsImVuZHBvaW50TWFwcGluZ3MiLCJ0b2tlbkFjY2Vzc0Z1bmN0aW9uIiwiZXJyb3JIYW5kbGVyIiwibG9nZ2VyIiwic2V0RW5kcG9pbnRNYXBwaW5ncyIsImVuZHBvaW50TWFwcGluZ3NQYXJhbSIsIm1vZGVsRmV0Y2giLCJtb2RlbEZldGNoUmVxdWVzdEFjdGlvbiIsInVuZGVmaW5lZCIsInByZXBhcmVGZXRjaCIsImVycm9yIiwibW9kZWxQYXRoIiwiaXNDb2xsZWN0aW9uSXRlbURlbGV0ZSIsInRyeUxpbWl0Iiwibm9SZXRyeSIsInRyeUNvdW50IiwiZGlkRmFpbCIsImxhc3RGZXRjaFJlc3VsdCIsImxhc3RFcnJvciIsImxhc3RFcnJvckRhdGEiLCJmZXRjaFJlc3VsdFVuZGVmaW5lZEVycm9yTWVzc2FnZSIsImZldGNoUmVzdWx0Tm90T2tFcnJvck1lc3NhZ2UiLCJwdXQiLCJub1N0b3JlIiwiTU9ERUxfRkVUQ0hfU1RBUlRfQUNUSU9OX1RZUEUiLCJUUkFOU0lFTlRfRkVUQ0hfU1RBUlQiLCJGRVRDSF9TVEFSVCIsImd1aWQiLCJmZXRjaFJlc3VsdCIsIm9hdXRoVG9rZW4iLCJtb2RlbE5hbWUiLCJhY2Nlc3NfdG9rZW4iLCJoZWFkZXJzIiwibWVyZ2UiLCJBdXRob3JpemF0aW9uIiwic2VuZEZldGNoIiwiRXJyb3IiLCJvayIsInJlc3VsdEFjdGlvblR5cGUiLCJNT0RFTF9GRVRDSF9SRVNVTFRfQUNUSU9OX1RZUEUiLCJUUkFOU0lFTlRfRkVUQ0hfUkVTVUxUX1JFQ0VJVkVEIiwiRkVUQ0hfUkVTVUxUX1JFQ0VJVkVEIiwiaXNQbGFpbk9iamVjdCIsIm1vZGVsUGF0aExldmVscyIsInNwbGl0IiwicG9wIiwiam9pbiIsInJlcGxhY2VWYWx1ZSIsIk1PREVMX1JFTU9WRV9LRVlfQUNUSU9OX1RZUEUiLCJjYXVnaHRFcnJvciIsImJhc2VFcnJvck1lc3NhZ2UiLCJjb25zdHJ1Y3RFcnJvckZyb21DYXVnaHRFcnJvciIsImlzTmV0d29ya09ubGluZSIsIndpbmRvd1NlcnZpY2UiLCJnZXRJc09ubGluZSIsImVycm9yRGF0YSIsInVua25vd25FcnJvckRhdGEiLCJuZXR3b3JrT2ZmbGluZUVycm9yRGF0YSIsImlzRmV0Y2hFcnJvckRhdGEiLCJtZXNzYWdlIiwicGF0aCIsInRpdGxlIiwic3RhdHVzIiwiZGV0YWlsIiwiTU9ERUxfRkVUQ0hfRVJST1JfQUNUSU9OX1RZUEUiLCJUUllfRkVUQ0hfRkFJTEVEIiwiSFRUUF9TVEFUVVNfQ09ERSIsIkJBRF9SRVFVRVNUIiwiSU5URVJOQUxfU0VSVkVSX0VSUk9SIiwiUkVRVUVTVF9USU1FT1VUIiwiZGVsYXkiLCJUUkFOU0lFTlRfRkVUQ0hfRkFJTEVEIiwiRkVUQ0hfRkFJTEVEIiwiVU5BVVRIT1JJWkVEIiwibW9kZWxGZXRjaExvb3AiLCJhY3Rpb24iLCJoYXNGZXRjaGVkIiwicGVyaW9kIiwiY2FuY2VsbGVkIiwiU1VDQ0VFREVEIiwibW9kZWxGZXRjaFJlY3VycmluZyIsImJnU3luY1Rhc2siLCJmb3JrIiwidGFrZSIsImNhbmNlbCIsIm1vZGVsRmV0Y2hTYWdhIiwiYXBpUm9vdFBhcmFtIiwidG9rZW5BY2Nlc3NGdW5jdGlvblBhcmFtIiwiYXJndW1lbnRzIiwiZXJyb3JIYW5kbGVyUGFyYW0iLCJzZXRBcGlSb290IiwiZ2V0TG9nZ2VyIiwidGFrZUV2ZXJ5IiwiTU9ERUxfRkVUQ0hfUkVRVUVTVF9BQ1RJT05fVFlQRSIsIkZFVENIX1JFUVVFU1QiLCJQRVJJT0RJQ19GRVRDSF9SRVFVRVNUIl0sInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3JlZHV4L3NhZ2FzL21vZGVsRmV0Y2hTYWdhLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFNhZ2FJdGVyYXRvciwgVGFzayB9IGZyb20gJ0ByZWR1eC1zYWdhL2NvcmUnXHJcbmltcG9ydCB7IGlzTmlsLCBpc1BsYWluT2JqZWN0LCBtZXJnZSB9IGZyb20gJ2xvZGFzaCdcclxuaW1wb3J0IG1vbWVudCBmcm9tICdtb21lbnQtdGltZXpvbmUnXHJcbmltcG9ydCB7IEFueUFjdGlvbiB9IGZyb20gJ3JlZHV4J1xyXG5pbXBvcnQgeyBjYWxsLCBjYW5jZWwsIGNhbmNlbGxlZCwgZGVsYXksIGZvcmssIHB1dCwgdGFrZSwgdGFrZUV2ZXJ5IH0gZnJvbSAncmVkdXgtc2FnYS9lZmZlY3RzJ1xyXG5pbXBvcnQgeyBuZXR3b3JrT2ZmbGluZUVycm9yRGF0YSwgdW5rbm93bkVycm9yRGF0YSB9IGZyb20gJy4uLy4uL2NvbnN0YW50cy9mZXRjaEVycm9yRGF0YSdcclxuaW1wb3J0IHsgZ2V0TGFzdEFjdGl2aXR5RGF0ZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2RhdGVTZXJ2aWNlJ1xyXG5pbXBvcnQgeyBzZW5kRmV0Y2gsIHNldEFwaVJvb3QgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9mZXRjaFNlcnZpY2UnXHJcbmltcG9ydCB7IHdpbmRvd1NlcnZpY2UgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy93aW5kb3dTZXJ2aWNlJ1xyXG5pbXBvcnQge1xyXG5cdEVuZHBvaW50TWFwcGluZyxcclxuXHRFbmRwb2ludE1hcHBpbmdzLFxyXG5cdEVycm9ySGFuZGxlcixcclxuXHRGZXRjaEVycm9yRGF0YSxcclxuXHRGZXRjaFJlc3VsdCxcclxuXHRIVFRQX1NUQVRVU19DT0RFLFxyXG5cdE1vZGVsLFxyXG5cdE1vZGVsQ29sbGVjdGlvbixcclxuXHRPQXV0aFRva2VuT3JOdWxsLFxyXG5cdFRva2VuQWNjZXNzRnVuY3Rpb25cclxufSBmcm9tICcuLi8uLi90eXBlcydcclxuaW1wb3J0IHsgY29uc3RydWN0RXJyb3JGcm9tQ2F1Z2h0RXJyb3IgfSBmcm9tICcuLi8uLi91dGlscy9lcnJvcidcclxuaW1wb3J0IHsgaXNGZXRjaEVycm9yRGF0YSwgcHJlcGFyZUZldGNoLCBQcmVwYXJlRmV0Y2hSZXN1bHQgfSBmcm9tICcuLi8uLi91dGlscy9mZXRjaCdcclxuaW1wb3J0IHsgZ2V0TG9nZ2VyLCBMb2dnZXIgfSBmcm9tICcuLi8uLi91dGlscy9sb2dnZXInXHJcbmltcG9ydCB7XHJcblx0TU9ERUxfRkVUQ0hfRVJST1JfQUNUSU9OX1RZUEUsXHJcblx0TU9ERUxfRkVUQ0hfUkVRVUVTVF9BQ1RJT05fVFlQRSxcclxuXHRNT0RFTF9GRVRDSF9SRVNVTFRfQUNUSU9OX1RZUEUsXHJcblx0TU9ERUxfRkVUQ0hfU1RBUlRfQUNUSU9OX1RZUEUsXHJcblx0TU9ERUxfUkVNT1ZFX0tFWV9BQ1RJT05fVFlQRSxcclxuXHRNb2RlbEZldGNoRXJyb3JBY3Rpb24sXHJcblx0TW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24sXHJcblx0TW9kZWxGZXRjaFJlc3VsdEFjdGlvbixcclxuXHRNb2RlbEZldGNoU3RhcnRBY3Rpb24sXHJcblx0TW9kZWxSZW1vdmVLZXlBY3Rpb24sXHJcblx0UEVSSU9ESUNfTU9ERUxfRkVUQ0hfVEVSTUlOQVRJT05fQUNUSU9OX1RZUEUsXHJcblx0UGVyaW9kaWNNb2RlbEZldGNoUmVxdWVzdEFjdGlvbixcclxuXHRQZXJpb2RpY01vZGVsRmV0Y2hUZXJtaW5hdGlvbkFjdGlvblxyXG59IGZyb20gJy4uL2FjdGlvbnMnXHJcblxyXG4vKiogVGhlIHRvdGFsIG51bWJlciBvZiB0cmllcyBmb3IgYGZldGNoRGF0YWAsIGluY2x1ZGluZyB0aGUgaW5pdGlhbCByZXF1ZXN0LiAqL1xyXG5leHBvcnQgY29uc3QgVFJZX0xJTUlUID0gNVxyXG5cclxuLy8jcmVnaW9uIEhlbHBlcnNcclxuXHJcbmV4cG9ydCBjb25zdCBtYXRjaGVzVGVybWluYXRpb25BY3Rpb24gPSAoXHJcblx0aW5jb21pbmdBY3Rpb246IEFueUFjdGlvbixcclxuXHRkYXRhUmVxdWVzdEFjdGlvbjogUGVyaW9kaWNNb2RlbEZldGNoUmVxdWVzdEFjdGlvblxyXG4pID0+XHJcblx0aW5jb21pbmdBY3Rpb24udHlwZSA9PT0gUEVSSU9ESUNfTU9ERUxfRkVUQ0hfVEVSTUlOQVRJT05fQUNUSU9OX1RZUEUuVEVSTUlOQVRFICYmXHJcblx0aW5jb21pbmdBY3Rpb24udGFza0lkID09PSBkYXRhUmVxdWVzdEFjdGlvbi50YXNrSWRcclxuXHJcbmV4cG9ydCBjb25zdCB0YWtlTWF0Y2hlc1Rlcm1pbmF0aW9uQWN0aW9uID1cclxuXHQoZGF0YVJlcXVlc3RBY3Rpb246IFBlcmlvZGljTW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24pID0+IChpbmNvbWluZ0FjdGlvbjogQW55QWN0aW9uKSA9PlxyXG5cdFx0bWF0Y2hlc1Rlcm1pbmF0aW9uQWN0aW9uKGluY29taW5nQWN0aW9uLCBkYXRhUmVxdWVzdEFjdGlvbilcclxuXHJcbi8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0ICovXHJcbi8qIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSByZXF1aXJlLXlpZWxkICovXHJcbmV4cG9ydCBjb25zdCBkZWZhdWx0VG9rZW5BY2Nlc3NGdW5jdGlvbjogVG9rZW5BY2Nlc3NGdW5jdGlvbiA9IGZ1bmN0aW9uKiAoKSB7XHJcblx0cmV0dXJuIG51bGxcclxufVxyXG5cclxuLyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cclxuZXhwb3J0IGNvbnN0IGRlZmF1bHRFcnJvckhhbmRsZXI6IEVycm9ySGFuZGxlciA9ICgpID0+IHtcclxuXHRyZXR1cm5cclxufVxyXG5cclxuaW50ZXJmYWNlIEdlbmVyaWNNb2RlbCBleHRlbmRzIE1vZGVsLCBSZWNvcmQ8c3RyaW5nLCBhbnk+IHt9XHJcblxyXG5jb25zdCBnZXRDaGlsZENvbGxlY3Rpb25LZXlzID0gKGVuZHBvaW50TWFwcGluZzogRW5kcG9pbnRNYXBwaW5nKSA9PlxyXG5cdE9iamVjdC5rZXlzKGVuZHBvaW50TWFwcGluZykucmVkdWNlKChvdXQ6IHN0cmluZ1tdLCBrZXkpID0+IHtcclxuXHRcdGNvbnN0IHZhbHVlID0gZW5kcG9pbnRNYXBwaW5nW2tleV1cclxuXHRcdGlmICghdmFsdWUgfHwgIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh2YWx1ZSwgJ19jb25maWcnKSkge1xyXG5cdFx0XHRyZXR1cm4gb3V0XHJcblx0XHR9XHJcblx0XHRjb25zdCBlbSA9IHZhbHVlIGFzIEVuZHBvaW50TWFwcGluZ1xyXG5cdFx0aWYgKCEhZW0uX2NvbmZpZyAmJiAhIWVtLl9jb25maWcuaXNDb2xsZWN0aW9uKSB7XHJcblx0XHRcdG91dC5wdXNoKGtleSlcclxuXHRcdH1cclxuXHRcdHJldHVybiBvdXRcclxuXHR9LCBbXSlcclxuXHJcbmNvbnN0IGNvbnZlcnRDb2xsZWN0aW9ucyA9IChlbmRwb2ludE1hcHBpbmc6IEVuZHBvaW50TWFwcGluZywgcHJlcGFyZUZldGNoUmVzdWx0OiBQcmVwYXJlRmV0Y2hSZXN1bHQsIGRhdGE6IGFueSkgPT4ge1xyXG5cdGNvbnN0IHsgZW5kcG9pbnRDb25maWcsIGZldGNoQ29uZmlnLCBpc0NvbGxlY3Rpb25JdGVtQ3JlYXRlLCBpc0NvbGxlY3Rpb25JdGVtRmV0Y2ggfSA9IHByZXBhcmVGZXRjaFJlc3VsdFxyXG5cdGNvbnN0IGNoaWxkQ29sbGVjdGlvbktleXMgPSBnZXRDaGlsZENvbGxlY3Rpb25LZXlzKGVuZHBvaW50TWFwcGluZylcclxuXHRjb25zdCBoYXNDaGlsZENvbGxlY3Rpb25zID0gY2hpbGRDb2xsZWN0aW9uS2V5cy5sZW5ndGggPiAwXHJcblx0Ly8gbm8gY29sbGVjdGlvbnMsIHJldHVybiBhcy1pc1xyXG5cdGlmICghZW5kcG9pbnRDb25maWcuaXNDb2xsZWN0aW9uICYmICFoYXNDaGlsZENvbGxlY3Rpb25zKSB7XHJcblx0XHRyZXR1cm4gZGF0YVxyXG5cdH1cclxuXHQvLyBkZWxldGUgcmV0dXJucyBubyBkYXRhXHJcblx0aWYgKGZldGNoQ29uZmlnLm1ldGhvZCA9PT0gJ0RFTEVURScpIHtcclxuXHRcdHJldHVybiB7fVxyXG5cdH1cclxuXHQvLyBkYXRhIGlzIGEgY29sbGVjdGlvbiBpdGVtLCBjb252ZXJ0IGFueSBjaGlsZCBjb2xsZWN0aW9ucyBpbiBpdGVtIHJlc3BvbnNlXHJcblx0aWYgKGlzQ29sbGVjdGlvbkl0ZW1GZXRjaCB8fCBpc0NvbGxlY3Rpb25JdGVtQ3JlYXRlKSB7XHJcblx0XHRyZXR1cm4gY29udmVydERhdGFDb2xsZWN0aW9uSXRlbShkYXRhLCBjaGlsZENvbGxlY3Rpb25LZXlzLCBlbmRwb2ludE1hcHBpbmcpXHJcblx0fVxyXG5cdC8vIGRhdGEgaXMgYSBjb2xsZWN0aW9uLCBjb252ZXJ0IGNvbGxlY3Rpb24gcmVzcG9uc2UsIGFuZCBhbnkgY2hpbGQgY29sbGVjdGlvbnMgaW4gZWFjaCBpdGVtXHJcblx0cmV0dXJuIGVuZHBvaW50Q29uZmlnLmlzQ29sbGVjdGlvblxyXG5cdFx0PyBjb252ZXJ0RGF0YVRvQ29sbGVjdGlvbihkYXRhLCBjaGlsZENvbGxlY3Rpb25LZXlzLCBlbmRwb2ludE1hcHBpbmcpXHJcblx0XHQ6IGNvbnZlcnREYXRhQ29sbGVjdGlvbkl0ZW0oZGF0YSwgY2hpbGRDb2xsZWN0aW9uS2V5cywgZW5kcG9pbnRNYXBwaW5nKVxyXG59XHJcblxyXG5jb25zdCBjb252ZXJ0RGF0YUNvbGxlY3Rpb25JdGVtID0gKGRhdGE6IGFueSwgY29sbGVjdGlvbktleXM6IHN0cmluZ1tdLCBlbmRwb2ludE1hcHBpbmc6IEVuZHBvaW50TWFwcGluZykgPT4ge1xyXG5cdGlmIChpc05pbChkYXRhKSkge1xyXG5cdFx0cmV0dXJuIGRhdGFcclxuXHR9XHJcblx0cmV0dXJuIE9iamVjdC5rZXlzKGRhdGEpLnJlZHVjZSgob3V0OiBHZW5lcmljTW9kZWwsIGtleSkgPT4ge1xyXG5cdFx0bGV0IHZhbHVlID0gZGF0YVtrZXldXHJcblx0XHRpZiAoY29sbGVjdGlvbktleXMuaW5jbHVkZXMoa2V5KSkge1xyXG5cdFx0XHRjb25zdCBjaGlsZEVuZHBvaW50TWFwcGluZyA9IGVuZHBvaW50TWFwcGluZ1trZXldIGFzIEVuZHBvaW50TWFwcGluZ1xyXG5cdFx0XHRjb25zdCBjaGlsZENvbGxlY3Rpb25LZXlzID0gZ2V0Q2hpbGRDb2xsZWN0aW9uS2V5cyhjaGlsZEVuZHBvaW50TWFwcGluZylcclxuXHRcdFx0dmFsdWUgPSBjb252ZXJ0RGF0YVRvQ29sbGVjdGlvbih2YWx1ZSwgY2hpbGRDb2xsZWN0aW9uS2V5cywgY2hpbGRFbmRwb2ludE1hcHBpbmcpXHJcblx0XHR9XHJcblx0XHRvdXRba2V5XSA9IHZhbHVlXHJcblx0XHRyZXR1cm4gb3V0XHJcblx0fSwge30pXHJcbn1cclxuXHJcbmNvbnN0IGNvbnZlcnREYXRhVG9Db2xsZWN0aW9uID0gKGRhdGE6IGFueSwgY29sbGVjdGlvbktleXM6IHN0cmluZ1tdLCBlbmRwb2ludE1hcHBpbmc6IEVuZHBvaW50TWFwcGluZykgPT4ge1xyXG5cdGlmIChpc05pbChkYXRhKSkge1xyXG5cdFx0cmV0dXJuIGRhdGFcclxuXHR9XHJcblx0cmV0dXJuIE9iamVjdC5rZXlzKGRhdGEpLnJlZHVjZSgob3V0OiBNb2RlbENvbGxlY3Rpb248YW55Piwga2V5KSA9PiB7XHJcblx0XHRjb25zdCB2YWx1ZSA9IGNvbnZlcnREYXRhQ29sbGVjdGlvbkl0ZW0oZGF0YVtrZXldLCBjb2xsZWN0aW9uS2V5cywgZW5kcG9pbnRNYXBwaW5nKVxyXG5cdFx0LyogaXN0YW5idWwgaWdub3JlIGVsc2UgKi9cclxuXHRcdGlmICh2YWx1ZSAmJiB2YWx1ZS5pZCkge1xyXG5cdFx0XHRvdXRbdmFsdWUuaWRdID0gdmFsdWVcclxuXHRcdH1cclxuXHRcdHJldHVybiBvdXRcclxuXHR9LCB7fSlcclxufVxyXG5cclxuLyoqXHJcbiAqIFVzaW5nIHRoZSBgbGFzdEFjdGl2aXR5RGF0ZWAgc2V0IGluIGxvY2FsIHN0b3JhZ2UgYnkgXCJzdHVkaW9raXQtY2FsaXBlci1qc1wiLFxyXG4gKiBkZXRlcm1pbmUgaWYgdGhlIHVzZXIgaGFzIGJlZW4gaWRsZSwgZS5nLiBoYXMgaGFkIG5vIGFjdGl2aXR5IGluIHRoZSBsYXN0IDE1IG1pbnV0ZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiBpc1VzZXJJZGxlKCkge1xyXG5cdGNvbnN0IGxhc3RBY3Rpdml0eURhdGVTdHJpbmcgPSBnZXRMYXN0QWN0aXZpdHlEYXRlKClcclxuXHRpZiAoIWxhc3RBY3Rpdml0eURhdGVTdHJpbmcpIHtcclxuXHRcdHJldHVybiBmYWxzZVxyXG5cdH1cclxuXHJcblx0Y29uc3QgbGFzdEFjdGl2aXR5RGF0ZVV0YyA9IG1vbWVudC51dGMobGFzdEFjdGl2aXR5RGF0ZVN0cmluZylcclxuXHRjb25zdCBub3dVdGMgPSBtb21lbnQudXRjKClcclxuXHRjb25zdCBpZGxlTWludXRlc1RocmVzaG9sZCA9IDEwMDAgKiA2MCAqIDE1IC8vIDE1IG1pbnV0ZXNcclxuXHJcblx0cmV0dXJuIG5vd1V0Yy5kaWZmKGxhc3RBY3Rpdml0eURhdGVVdGMpID49IGlkbGVNaW51dGVzVGhyZXNob2xkXHJcbn1cclxuXHJcbi8vI2VuZHJlZ2lvbiBIZWxwZXJzXHJcblxyXG4vLyNyZWdpb24gTG9jYWwgVmFyaWFibGVzXHJcblxyXG5sZXQgZW5kcG9pbnRNYXBwaW5nczogRW5kcG9pbnRNYXBwaW5nc1xyXG5sZXQgdG9rZW5BY2Nlc3NGdW5jdGlvbjogVG9rZW5BY2Nlc3NGdW5jdGlvblxyXG5sZXQgZXJyb3JIYW5kbGVyOiBFcnJvckhhbmRsZXJcclxubGV0IGxvZ2dlcjogTG9nZ2VyXHJcblxyXG4vLyNlbmRyZWdpb24gTG9jYWwgVmFyaWFibGVzXHJcblxyXG4vKiogU2V0IHRoZSBzaGFyZWQgRW5kcG9pbnRNYXBwaW5ncyB2YXJpYWJsZSwgZXhwb3NlZCBmb3IgdGVzdGFiaWxpdHkgKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEVuZHBvaW50TWFwcGluZ3MoZW5kcG9pbnRNYXBwaW5nc1BhcmFtOiBFbmRwb2ludE1hcHBpbmdzKSB7XHJcblx0ZW5kcG9pbnRNYXBwaW5ncyA9IGVuZHBvaW50TWFwcGluZ3NQYXJhbVxyXG59XHJcblxyXG4vKipcclxuICogQ29uc3RydWN0IGEgcmVxdWVzdCBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgZGF0YVJlcXVlc3RBY3Rpb24sIG1ha2UgYSByZXF1ZXN0IHdpdGggYSBjb25maWd1cmFibGUgcmV0cnksXHJcbiAqIGFuZCBoYW5kbGUgZXJyb3JzLCBsb2dnaW5nIGFuZCBkaXNwYXRjaGluZyBhbGwgc3RlcHMuXHJcbiAqXHJcbiAqIEBwYXJhbSBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbiBBIG1vZGVsIGZldGNoIHJlcXVlc3QgYWN0aW9uIHdpdGggdGhlIHJlcXVlc3QgY29uZmlndXJhdGlvblxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uKiBtb2RlbEZldGNoKFxyXG5cdG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uOiBNb2RlbEZldGNoUmVxdWVzdEFjdGlvbiB8IFBlcmlvZGljTW9kZWxGZXRjaFJlcXVlc3RBY3Rpb25cclxuKTogU2FnYUl0ZXJhdG9yIHtcclxuXHRsZXQgcHJlcGFyZUZldGNoUmVzdWx0OiBQcmVwYXJlRmV0Y2hSZXN1bHQgfCB1bmRlZmluZWQgPSB1bmRlZmluZWRcclxuXHR0cnkge1xyXG5cdFx0cHJlcGFyZUZldGNoUmVzdWx0ID0gcHJlcGFyZUZldGNoKG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLCBlbmRwb2ludE1hcHBpbmdzKVxyXG5cdH0gY2F0Y2ggKGVycm9yKSB7XHJcblx0XHQvLyBOb3RlOiBgcHJlcGFyZUZldGNoYCBhbHJlYWR5IGVuc3VyZXMgdGhhdCBgZXJyb3JgIGlzIGFuIGFjdHVhbCBgRXJyb3JgXHJcblx0XHRsb2dnZXIuZXJyb3IoZXJyb3IpXHJcblx0XHRlcnJvckhhbmRsZXIoZXJyb3IgYXMgRXJyb3IsIG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uKVxyXG5cdFx0cmV0dXJuXHJcblx0fVxyXG5cclxuXHQvLyBnZXQgcHJlcGFyZWQgdmFyaWFibGVzXHJcblx0Y29uc3QgeyBtb2RlbFBhdGgsIGZldGNoQ29uZmlnLCBlbmRwb2ludE1hcHBpbmcsIGlzQ29sbGVjdGlvbkl0ZW1DcmVhdGUsIGlzQ29sbGVjdGlvbkl0ZW1EZWxldGUgfSA9XHJcblx0XHRwcmVwYXJlRmV0Y2hSZXN1bHRcclxuXHJcblx0Ly8gQ29uZmlndXJlIHJldHJ5XHJcblx0Y29uc3QgdHJ5TGltaXQ6IG51bWJlciA9IG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLm5vUmV0cnkgPyAxIDogVFJZX0xJTUlUXHJcblx0bGV0IHRyeUNvdW50ID0gMFxyXG5cdGxldCBkaWRGYWlsID0gZmFsc2VcclxuXHRsZXQgbGFzdEZldGNoUmVzdWx0OiBGZXRjaFJlc3VsdCB8IHVuZGVmaW5lZFxyXG5cdGxldCBsYXN0RXJyb3I6IEVycm9yIHwgdW5kZWZpbmVkXHJcblx0bGV0IGxhc3RFcnJvckRhdGE6IEZldGNoRXJyb3JEYXRhIHwgdW5kZWZpbmVkXHJcblxyXG5cdGNvbnN0IGZldGNoUmVzdWx0VW5kZWZpbmVkRXJyb3JNZXNzYWdlID0gXCInZmV0Y2hSZXN1bHQnIGlzIHVuZGVmaW5lZFwiXHJcblx0Y29uc3QgZmV0Y2hSZXN1bHROb3RPa0Vycm9yTWVzc2FnZSA9IFwiJ2ZldGNoUmVzdWx0Lm9rJyBpcyBmYWxzZVwiXHJcblxyXG5cdC8vIFJ1biByZXRyeSBsb29wXHJcblx0ZG8ge1xyXG5cdFx0ZGlkRmFpbCA9IGZhbHNlXHJcblx0XHR0cnlDb3VudCsrXHJcblxyXG5cdFx0Ly8gZGlzcGF0Y2ggdGhhdCB0aGUgZmV0Y2ggcmVxdWVzdCBzdGFydGVkLCB3aGljaCB1cGRhdGVzIGBfbWV0YWRhdGFgIGFuZCBgZ3VpZGAsIGlmIHByb3ZpZGVkLCBhdCB0aGUgdGFyZ2V0IHJlZHV4IGBtb2RlbFBhdGhgXHJcblx0XHR5aWVsZCBwdXQ8TW9kZWxGZXRjaFN0YXJ0QWN0aW9uPih7XHJcblx0XHRcdHR5cGU6IG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLm5vU3RvcmVcclxuXHRcdFx0XHQ/IE1PREVMX0ZFVENIX1NUQVJUX0FDVElPTl9UWVBFLlRSQU5TSUVOVF9GRVRDSF9TVEFSVFxyXG5cdFx0XHRcdDogTU9ERUxfRkVUQ0hfU1RBUlRfQUNUSU9OX1RZUEUuRkVUQ0hfU1RBUlQsXHJcblx0XHRcdG1vZGVsUGF0aCxcclxuXHRcdFx0Z3VpZDogbW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24uZ3VpZFxyXG5cdFx0fSlcclxuXHJcblx0XHRsZXQgZmV0Y2hSZXN1bHQ6IEZldGNoUmVzdWx0IHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkXHJcblx0XHR0cnkge1xyXG5cdFx0XHQvLyBnZXQgdGhlIE9BdXRoIFRva2VuLCBpZiBhbnksIGFuZCBjYWxsIGBmZXRjaGBcclxuXHRcdFx0Y29uc3Qgb2F1dGhUb2tlbjogT0F1dGhUb2tlbk9yTnVsbCA9IHlpZWxkIGNhbGwodG9rZW5BY2Nlc3NGdW5jdGlvbiwgbW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24ubW9kZWxOYW1lKVxyXG5cdFx0XHRpZiAob2F1dGhUb2tlbj8uYWNjZXNzX3Rva2VuKSB7XHJcblx0XHRcdFx0ZmV0Y2hDb25maWcuaGVhZGVycyA9IG1lcmdlKHt9LCBmZXRjaENvbmZpZy5oZWFkZXJzLCB7XHJcblx0XHRcdFx0XHRBdXRob3JpemF0aW9uOiBgQmVhcmVyICR7b2F1dGhUb2tlbi5hY2Nlc3NfdG9rZW59YFxyXG5cdFx0XHRcdH0pXHJcblx0XHRcdH1cclxuXHRcdFx0ZmV0Y2hSZXN1bHQgPSB5aWVsZCBjYWxsKHNlbmRGZXRjaCwgZmV0Y2hDb25maWcpXHJcblx0XHRcdC8vIHN0b3JlIGBmZXRjaFJlc3VsdGAgZm9yIHJlZmVyZW5jZSBhZnRlciB0aGUgZG8td2hpbGUgbG9vcFxyXG5cdFx0XHRsYXN0RmV0Y2hSZXN1bHQgPSBmZXRjaFJlc3VsdFxyXG5cclxuXHRcdFx0Ly8gdGhyb3cgb24gdW5zdWNjZXNzZnVsIHJlc3VsdCB0byBzaG9ydC1jaXJjdWl0IGFuZCB0cmlnZ2VyIGNhdGNoK3JldHJ5XHJcblx0XHRcdGlmICghZmV0Y2hSZXN1bHQpIHRocm93IG5ldyBFcnJvcihmZXRjaFJlc3VsdFVuZGVmaW5lZEVycm9yTWVzc2FnZSlcclxuXHRcdFx0aWYgKCFmZXRjaFJlc3VsdC5vaykgdGhyb3cgbmV3IEVycm9yKGZldGNoUmVzdWx0Tm90T2tFcnJvck1lc3NhZ2UpXHJcblxyXG5cdFx0XHQvLyBoYW5kbGUgc3VjY2Vzc1xyXG5cdFx0XHRjb25zdCByZXN1bHRBY3Rpb25UeXBlID0gbW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24ubm9TdG9yZVxyXG5cdFx0XHRcdD8gTU9ERUxfRkVUQ0hfUkVTVUxUX0FDVElPTl9UWVBFLlRSQU5TSUVOVF9GRVRDSF9SRVNVTFRfUkVDRUlWRURcclxuXHRcdFx0XHQ6IE1PREVMX0ZFVENIX1JFU1VMVF9BQ1RJT05fVFlQRS5GRVRDSF9SRVNVTFRfUkVDRUlWRURcclxuXHJcblx0XHRcdGNvbnN0IGRhdGEgPSBjb252ZXJ0Q29sbGVjdGlvbnMoZW5kcG9pbnRNYXBwaW5nLCBwcmVwYXJlRmV0Y2hSZXN1bHQsIGZldGNoUmVzdWx0LmRhdGEpXHJcblx0XHRcdC8vIGF0dGFjaCBndWlkIHRvIHJlc3VsdCBkYXRhIGlmIGl0IGlzIGFuIG9iamVjdFxyXG5cdFx0XHRpZiAobW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24uZ3VpZCAmJiBpc1BsYWluT2JqZWN0KGRhdGEpKSB7XHJcblx0XHRcdFx0ZGF0YS5ndWlkID0gbW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24uZ3VpZFxyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBQT1NUIG5ldyBjb2xsZWN0aW9uIGl0ZW1cclxuXHRcdFx0aWYgKGlzQ29sbGVjdGlvbkl0ZW1DcmVhdGUpIHtcclxuXHRcdFx0XHRjb25zdCBtb2RlbFBhdGhMZXZlbHMgPSBtb2RlbFBhdGguc3BsaXQoJy4nKVxyXG5cdFx0XHRcdC8vIHJlbW92ZSBndWlkXHJcblx0XHRcdFx0bW9kZWxQYXRoTGV2ZWxzLnBvcCgpXHJcblx0XHRcdFx0Ly8gYWRkIGJ5IG5ldyByZXN1bHQncyBpZFxyXG5cdFx0XHRcdHlpZWxkIHB1dDxNb2RlbEZldGNoUmVzdWx0QWN0aW9uPih7XHJcblx0XHRcdFx0XHR0eXBlOiByZXN1bHRBY3Rpb25UeXBlLFxyXG5cdFx0XHRcdFx0bW9kZWxQYXRoOiBgJHttb2RlbFBhdGhMZXZlbHMuam9pbignLicpfS4ke2RhdGEuaWR9YCxcclxuXHRcdFx0XHRcdGd1aWQ6IG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLmd1aWQsXHJcblx0XHRcdFx0XHRyZXBsYWNlVmFsdWU6IG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLnJlcGxhY2VWYWx1ZSxcclxuXHRcdFx0XHRcdGRhdGFcclxuXHRcdFx0XHR9KVxyXG5cdFx0XHRcdC8vIHJlbW92ZSB0ZW1wIGl0ZW0gdW5kZXIgZ3VpZCBrZXlcclxuXHRcdFx0XHR5aWVsZCBwdXQ8TW9kZWxSZW1vdmVLZXlBY3Rpb24+KHtcclxuXHRcdFx0XHRcdHR5cGU6IE1PREVMX1JFTU9WRV9LRVlfQUNUSU9OX1RZUEUsXHJcblx0XHRcdFx0XHRtb2RlbFBhdGg6IG1vZGVsUGF0aFxyXG5cdFx0XHRcdH0pXHJcblx0XHRcdH1cclxuXHRcdFx0Ly8gREVMRVRFIGNvbGxlY3Rpb24gaXRlbVxyXG5cdFx0XHRlbHNlIGlmIChpc0NvbGxlY3Rpb25JdGVtRGVsZXRlKSB7XHJcblx0XHRcdFx0eWllbGQgcHV0PE1vZGVsUmVtb3ZlS2V5QWN0aW9uPih7XHJcblx0XHRcdFx0XHR0eXBlOiBNT0RFTF9SRU1PVkVfS0VZX0FDVElPTl9UWVBFLFxyXG5cdFx0XHRcdFx0bW9kZWxQYXRoOiBtb2RlbFBhdGhcclxuXHRcdFx0XHR9KVxyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHlpZWxkIHB1dDxNb2RlbEZldGNoUmVzdWx0QWN0aW9uPih7XHJcblx0XHRcdFx0XHR0eXBlOiByZXN1bHRBY3Rpb25UeXBlLFxyXG5cdFx0XHRcdFx0bW9kZWxQYXRoOiBtb2RlbFBhdGgsXHJcblx0XHRcdFx0XHRndWlkOiBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5ndWlkLFxyXG5cdFx0XHRcdFx0cmVwbGFjZVZhbHVlOiBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5yZXBsYWNlVmFsdWUsXHJcblx0XHRcdFx0XHRkYXRhXHJcblx0XHRcdFx0fSlcclxuXHRcdFx0fVxyXG5cdFx0fSBjYXRjaCAoY2F1Z2h0RXJyb3I6IHVua25vd24pIHtcclxuXHRcdFx0Ly8gY29uc3RydWN0IGBFcnJvcmAgdXNpbmcgYGNhdWdodEVycm9yYFxyXG5cdFx0XHRjb25zdCBiYXNlRXJyb3JNZXNzYWdlID0gYCdtb2RlbEZldGNoJyBmYWlsZWQgZm9yICcke21vZGVsUGF0aH0nYFxyXG5cdFx0XHRjb25zdCBlcnJvciA9IGNvbnN0cnVjdEVycm9yRnJvbUNhdWdodEVycm9yKGNhdWdodEVycm9yLCAnTW9kZWxGZXRjaEVycm9yJywgYmFzZUVycm9yTWVzc2FnZSlcclxuXHRcdFx0Ly8gY3JlYXRlIGEgZGVmYXVsdCBgZXJyb3JEYXRhYCBvYmplY3RcclxuXHRcdFx0Y29uc3QgaXNOZXR3b3JrT25saW5lID0gd2luZG93U2VydmljZS5nZXRJc09ubGluZSgpXHJcblx0XHRcdGxldCBlcnJvckRhdGE6IEZldGNoRXJyb3JEYXRhID0gaXNOZXR3b3JrT25saW5lID8gdW5rbm93bkVycm9yRGF0YSA6IG5ldHdvcmtPZmZsaW5lRXJyb3JEYXRhXHJcblx0XHRcdC8vIHVwZGF0ZSBgZXJyb3JEYXRhYCBmcm9tIHRoZSBgZmV0Y2hSZXN1bHQuZGF0YWBcclxuXHRcdFx0Ly8gZm9yIHdoZW4gdGhlIGZldGNoIGdvdCBhIHJlc3VsdCwgYnV0IGl0IHdhcyBhbiB1bnN1Y2Nlc3NmdWwgc3RhdHVzXHJcblx0XHRcdGlmIChmZXRjaFJlc3VsdD8uZGF0YSAmJiBpc0ZldGNoRXJyb3JEYXRhKGZldGNoUmVzdWx0LmRhdGEpKSB7XHJcblx0XHRcdFx0ZXJyb3JEYXRhID0gZmV0Y2hSZXN1bHQuZGF0YVxyXG5cdFx0XHRcdC8vIHVwZGF0ZSB0aGUgZXJyb3IncyBtZXNzYWdlIHRvIGluY2x1ZGUgaW5mbyBmcm9tIHRoZSBmZXRjaCByZXN1bHRcclxuXHRcdFx0XHRlcnJvci5tZXNzYWdlID0gYCR7YmFzZUVycm9yTWVzc2FnZX0uXFxuJHtmZXRjaENvbmZpZy5tZXRob2QgfHwgJ0dFVCd9ICR7ZmV0Y2hDb25maWcucGF0aH0gZmFpbGVkLlxcbiR7XHJcblx0XHRcdFx0XHRlcnJvckRhdGEudGl0bGVcclxuXHRcdFx0XHR9ICgke2Vycm9yRGF0YS5zdGF0dXN9KWBcclxuXHRcdFx0XHRpZiAoZXJyb3JEYXRhLmRldGFpbCkge1xyXG5cdFx0XHRcdFx0ZXJyb3IubWVzc2FnZSArPSBgOiAke2Vycm9yRGF0YS5kZXRhaWx9YFxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gdHJhY2sgYGVycm9yRGF0YWAgYW5kIGBlcnJvcmAgZm9yIGFmdGVyIHRoZSB3aGlsZS1sb29wXHJcblx0XHRcdGxhc3RFcnJvckRhdGEgPSBlcnJvckRhdGFcclxuXHRcdFx0bGFzdEVycm9yID0gZXJyb3JcclxuXHJcblx0XHRcdC8vIGRpc3BhdGNoIHRoYXQgdGhpcyB0cnkgZmFpbGVkXHJcblx0XHRcdHlpZWxkIHB1dDxNb2RlbEZldGNoRXJyb3JBY3Rpb24+KHtcclxuXHRcdFx0XHR0eXBlOiBNT0RFTF9GRVRDSF9FUlJPUl9BQ1RJT05fVFlQRS5UUllfRkVUQ0hfRkFJTEVELFxyXG5cdFx0XHRcdG1vZGVsUGF0aDogbW9kZWxQYXRoLFxyXG5cdFx0XHRcdGd1aWQ6IG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLmd1aWQsXHJcblx0XHRcdFx0ZXJyb3JEYXRhXHJcblx0XHRcdH0pXHJcblxyXG5cdFx0XHQvLyBsb2cgdG8gdGhlIGNvbnNvbGVcclxuXHRcdFx0bG9nZ2VyLmVycm9yKGVycm9yKVxyXG5cclxuXHRcdFx0ZGlkRmFpbCA9IHRydWVcclxuXHJcblx0XHRcdC8vIERvIG5vdCByZXRyeSBpZiB0aGUgcmVzcG9uc2UgaXMgYmV0d2VlbiA0MDAtNTAwIChleGNlcHQgNDA4OiByZXF1ZXN0IHRpbWVvdXQpXHJcblx0XHRcdGlmIChcclxuXHRcdFx0XHRlcnJvckRhdGEuc3RhdHVzID49IEhUVFBfU1RBVFVTX0NPREUuQkFEX1JFUVVFU1QgJiZcclxuXHRcdFx0XHRlcnJvckRhdGEuc3RhdHVzIDwgSFRUUF9TVEFUVVNfQ09ERS5JTlRFUk5BTF9TRVJWRVJfRVJST1IgJiZcclxuXHRcdFx0XHRlcnJvckRhdGEuc3RhdHVzICE9PSBIVFRQX1NUQVRVU19DT0RFLlJFUVVFU1RfVElNRU9VVFxyXG5cdFx0XHQpIHtcclxuXHRcdFx0XHR0cnlDb3VudCA9IHRyeUxpbWl0XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIGlmIHRoZXJlIGFyZSB0cmllcyByZW1haW5pbmcsIHBlcmZvcm0gYW4gZXhwb25lbnRpYWwgYmFja29mZlxyXG5cdFx0XHRpZiAodHJ5Q291bnQgPCB0cnlMaW1pdCkge1xyXG5cdFx0XHRcdHlpZWxkIGRlbGF5KDIgKiogKHRyeUNvdW50IC0gMSkgKiAxMDApIC8vIDEwMCwgMjAwLCA0MDAsIDgwMFxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSB3aGlsZSAodHJ5Q291bnQgPCB0cnlMaW1pdCAmJiBkaWRGYWlsKVxyXG5cclxuXHQvLyBpZiBmZXRjaCBmYWlsZWQgY29tcGxldGVseSBhZnRlciBhbGwgcmV0cmllc1xyXG5cdGlmICh0cnlDb3VudCA9PT0gdHJ5TGltaXQgJiYgZGlkRmFpbCkge1xyXG5cdFx0Ly8gdGhlc2Ugd2lsbCBhbHdheXMgZXhpc3RcclxuXHRcdGNvbnN0IGVycm9yRGF0YSA9IGxhc3RFcnJvckRhdGEgYXMgRmV0Y2hFcnJvckRhdGFcclxuXHRcdGNvbnN0IGVycm9yID0gbGFzdEVycm9yIGFzIEVycm9yXHJcblxyXG5cdFx0Ly8gZGlzcGF0Y2ggdGhhdCB0aGUgZmV0Y2ggZmFpbGVkLCB3aGljaCB1cGRhdGVzIGBfbWV0YWRhdGFgIChhbmQgYGd1aWRgLCBpZiBwcm92aWRlZCkgYXQgdGhlIHRhcmdldCByZWR1eCBgbW9kZWxQYXRoYFxyXG5cdFx0eWllbGQgcHV0PE1vZGVsRmV0Y2hFcnJvckFjdGlvbj4oe1xyXG5cdFx0XHR0eXBlOiBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5ub1N0b3JlXHJcblx0XHRcdFx0PyBNT0RFTF9GRVRDSF9FUlJPUl9BQ1RJT05fVFlQRS5UUkFOU0lFTlRfRkVUQ0hfRkFJTEVEXHJcblx0XHRcdFx0OiBNT0RFTF9GRVRDSF9FUlJPUl9BQ1RJT05fVFlQRS5GRVRDSF9GQUlMRUQsXHJcblx0XHRcdG1vZGVsUGF0aDogbW9kZWxQYXRoLFxyXG5cdFx0XHRndWlkOiBtb2RlbEZldGNoUmVxdWVzdEFjdGlvbi5ndWlkLFxyXG5cdFx0XHRlcnJvckRhdGFcclxuXHRcdH0pXHJcblxyXG5cdFx0Ly8gU2VuZCBlcnJvciB0byBlcnJvciBoYW5kbGVyLCBleGNlcHQgZm9yIDQwMXNcclxuXHRcdGlmIChlcnJvckRhdGEuc3RhdHVzICE9PSBIVFRQX1NUQVRVU19DT0RFLlVOQVVUSE9SSVpFRCkge1xyXG5cdFx0XHRlcnJvckhhbmRsZXIoZXJyb3IsIG1vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uLCBmZXRjaENvbmZpZywgbGFzdEZldGNoUmVzdWx0LCBlcnJvckRhdGEpXHJcblx0XHR9XHJcblx0fVxyXG59XHJcblxyXG4vKipcclxuICogVGhlIGxvb3Agc2FnYSB0aGF0IG1ha2VzIHRoZSByZXF1ZXN0IGV2ZXJ5IHthY3Rpb24ucGVyaW9kfSBtaWxsaXNlY29uZHMgdW50aWwgY2FuY2VsbGVkXHJcbiAqXHJcbiAqIEBwYXJhbSBhY3Rpb24gQW4gYWN0aW9uIHdpdGggdGhlIHJlcXVlc3QgY29uZmlndXJhdGlvblxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uKiBtb2RlbEZldGNoTG9vcChhY3Rpb246IFBlcmlvZGljTW9kZWxGZXRjaFJlcXVlc3RBY3Rpb24pOiBTYWdhSXRlcmF0b3Ige1xyXG5cdHRyeSB7XHJcblx0XHRsZXQgaGFzRmV0Y2hlZCA9IGZhbHNlXHJcblx0XHRkbyB7XHJcblx0XHRcdC8vIGNhbGwgdGhlIGZldGNoIGlmIHRoaXMgaXMgdGhlIGZpcnN0IGxvb3AgaXRlcmF0aW9uLFxyXG5cdFx0XHQvLyBvciBpZiB0aGUgdXNlciBpcyBub3QgaWRsZVxyXG5cdFx0XHRpZiAoIWhhc0ZldGNoZWQgfHwgIWlzVXNlcklkbGUoKSkge1xyXG5cdFx0XHRcdHlpZWxkIGNhbGwobW9kZWxGZXRjaCwgYWN0aW9uKVxyXG5cdFx0XHRcdGhhc0ZldGNoZWQgPSB0cnVlXHJcblx0XHRcdH1cclxuXHRcdFx0eWllbGQgZGVsYXkoYWN0aW9uLnBlcmlvZClcclxuXHRcdH0gd2hpbGUgKHRydWUpXHJcblx0fSBjYXRjaCAoY2F1Z2h0RXJyb3I6IHVua25vd24pIHtcclxuXHRcdGNvbnN0IGVycm9yID0gY29uc3RydWN0RXJyb3JGcm9tQ2F1Z2h0RXJyb3IoY2F1Z2h0RXJyb3IsICdNb2RlbEZldGNoTG9vcEVycm9yJywgJ21vZGVsRmV0Y2hMb29wIGZhaWxlZCcpXHJcblx0XHRsb2dnZXIuZXJyb3IoZXJyb3IpXHJcblx0XHQvLyBjYXRjaCBhbmQgbG9nIGFueSB1bmV4cGVjdGVkIGVycm9ycyBub3QgY2F1Z2h0IGluc2lkZSBgZmV0Y2hEYXRhYFxyXG5cdFx0ZXJyb3JIYW5kbGVyKGVycm9yLCBhY3Rpb24pXHJcblx0fSBmaW5hbGx5IHtcclxuXHRcdGlmICh5aWVsZCBjYW5jZWxsZWQoKSkge1xyXG5cdFx0XHR5aWVsZCBwdXQ8UGVyaW9kaWNNb2RlbEZldGNoVGVybWluYXRpb25BY3Rpb24+KHtcclxuXHRcdFx0XHR0eXBlOiBQRVJJT0RJQ19NT0RFTF9GRVRDSF9URVJNSU5BVElPTl9BQ1RJT05fVFlQRS5TVUNDRUVERUQsXHJcblx0XHRcdFx0dGFza0lkOiBhY3Rpb24udGFza0lkXHJcblx0XHRcdH0pXHJcblx0XHR9XHJcblx0fVxyXG59XHJcblxyXG4vKipcclxuICogQ2FsbCB0aGUgZmV0Y2hEYXRhIHNhZ2EgZXZlcnkge2FjdGlvbi5wZXJpb2R9IG1pbGxpc2Vjb25kcy4gVGhpcyBzYWdhIHJlcXVpcmVzIHRoZSAncGVyaW9kJyBhbmQgJ3Rhc2tJZCcgcHJvcGVydGllc1xyXG4gKiBvbiB0aGUgYWN0aW9uIHBhcmFtZXRlci5cclxuICpcclxuICogQHBhcmFtIGFjdGlvbiBBbiBhY3Rpb24gd2l0aCB0aGUgcmVxdWVzdCBjb25maWd1cmF0aW9uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24qIG1vZGVsRmV0Y2hSZWN1cnJpbmcoYWN0aW9uOiBQZXJpb2RpY01vZGVsRmV0Y2hSZXF1ZXN0QWN0aW9uKTogU2FnYUl0ZXJhdG9yIHtcclxuXHRpZiAoIWFjdGlvbi5wZXJpb2QpIHtcclxuXHRcdHRocm93IG5ldyBFcnJvcihcIidwZXJpb2QnIGlzIHJlcXVpcmVkXCIpXHJcblx0fVxyXG5cdGlmICghYWN0aW9uLnRhc2tJZCkge1xyXG5cdFx0dGhyb3cgbmV3IEVycm9yKFwiJ3Rhc2tJZCcgaXMgcmVxdWlyZWRcIilcclxuXHR9XHJcblxyXG5cdGNvbnN0IGJnU3luY1Rhc2s6IFRhc2sgPSB5aWVsZCBmb3JrKG1vZGVsRmV0Y2hMb29wLCBhY3Rpb24pXHJcblx0eWllbGQgdGFrZSh0YWtlTWF0Y2hlc1Rlcm1pbmF0aW9uQWN0aW9uKGFjdGlvbikpXHJcblx0eWllbGQgY2FuY2VsKGJnU3luY1Rhc2spXHJcbn1cclxuXHJcbi8qKlxyXG4gKiBUaGUgbWFpbiBzYWdhIGZvciBmZXRjaGluZyBtb2RlbHMuIE11c3QgYmUgaW5pdGlhbGl6ZWQgd2l0aCBhbiBFbmRwb2ludE1hcHBpbmdzIG9iamVjdCB0aGF0IGNhbiBiZSBmZXRjaGVkXHJcbiAqIGFuZCBhbiBBUEkgcm9vdCB0byBwcmVwZW5kIHRvIGFueSBwYXJ0aWFsIFVSTHMgc3BlY2lmaWVkIGluIHRoZSBFbmRwb2ludE1hcHBpbmdzIG9iamVjdC4gQSBsb2dnZXIgc2hvdWxkIG5vcm1hbGx5IGJlIHByb3ZpZGVkXHJcbiAqIGFzIHdlbGwuXHJcbiAqXHJcbiAqIGBFbmRwb2ludE1hcHBpbmdzYCBvYmplY3QgcmVxdWlyZSBhIGZvcm0gYXMgZm9sbG93cyAod2l0aCBvcHRpb25hbCBuZXN0ZWQgbW9kZWxzKTpcclxuICogYGBgXHJcbiAqIHtcclxuICogXHRmcnlNb2RlbDoge1xyXG4gKiBcdFx0cGF0aDogJy9hcGkvRm9vJ1xyXG4gKiBcdH0sXHJcbiAqIFx0Z3JvdXBPZk1vZGVsczoge1xyXG4gKiBcdFx0bGVlbGFNb2RlbDoge1xyXG4gKiBcdFx0XHRwYXRoOiAnL2FwaS9CYXInXHJcbiAqIFx0XHR9LFxyXG4gKiBcdFx0YmVuZGVyTW9kZWw6IHtcclxuICogXHRcdFx0cGF0aDogJy9hcGkvQmF6J1xyXG4gKiBcdFx0fVxyXG4gKiBcdH1cclxuICogfVxyXG4gKiBgYGBcclxuICogYEVuZHBvaW50TWFwcGluZ2Agb2JqZWN0cyBhcmUgcmVmZXJlbmNlZCBpbiB0aGUgYWN0aW9ucy5EQVRBX1JFUVVFU1RFRCBhY3Rpb24gYnkgcGF0aCwgaS5lLlxyXG4gKiBgeyB0eXBlOiBhY3Rpb25zLkRBVEFfUkVRVUVTVEVELCB7IG1vZGVsTmFtZTogJ2ZyeU1vZGVsJyB9IH1gXHJcbiAqIC0tIG9yIC0tXHJcbiAqIGB7IHR5cGU6IGFjdGlvbnMuREFUQV9SRVFVRVNURUQsIHsgbW9kZWxOYW1lOiAnZ3JvdXBPZk1vZGVscy5sZWVsYU1vZGVsJyB9IH1gXHJcbiAqXHJcbiAqIEBleHBvcnRcclxuICogQHBhcmFtIGVuZHBvaW50TWFwcGluZ3NQYXJhbSBBIG1hcHBpbmcgb2YgQVBJIGVuZHBvaW50cyBhdmFpbGFibGUgaW4gdGhlIGFwcGxpY2F0aW9uXHJcbiAqIEBwYXJhbSBhcGlSb290UGFyYW0gQSB1cmwgdG8gd2hpY2ggcGFydGlhbCBVUkxzIGFyZSBhcHBlbmRlZCAoaS5lLikgJ2h0dHBzOi8vbXlhcHAuY29tJ1xyXG4gKiBAcGFyYW0gdG9rZW5BY2Nlc3NGdW5jdGlvblBhcmFtIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhbiBvcHRpb25hbCBPQXV0aCB0b2tlblxyXG4gKiBAcGFyYW0gZXJyb3JIYW5kbGVyUGFyYW0gQSBmdW5jdGlvbiB0aGF0IGlzIGNhbGxlZCB3aGVuIGFueSBmZXRjaCBleGNlcHRpb25zIG9jY3VyXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiogbW9kZWxGZXRjaFNhZ2EoXHJcblx0ZW5kcG9pbnRNYXBwaW5nc1BhcmFtOiBFbmRwb2ludE1hcHBpbmdzLFxyXG5cdGFwaVJvb3RQYXJhbT86IHN0cmluZyxcclxuXHR0b2tlbkFjY2Vzc0Z1bmN0aW9uUGFyYW06IFRva2VuQWNjZXNzRnVuY3Rpb24gfCB1bmRlZmluZWQgPSBkZWZhdWx0VG9rZW5BY2Nlc3NGdW5jdGlvbixcclxuXHRlcnJvckhhbmRsZXJQYXJhbTogRXJyb3JIYW5kbGVyIHwgdW5kZWZpbmVkID0gZGVmYXVsdEVycm9ySGFuZGxlclxyXG4pOiBTYWdhSXRlcmF0b3Ige1xyXG5cdHNldEFwaVJvb3QoYXBpUm9vdFBhcmFtKVxyXG5cdHNldEVuZHBvaW50TWFwcGluZ3MoZW5kcG9pbnRNYXBwaW5nc1BhcmFtKVxyXG5cdGVycm9ySGFuZGxlciA9IGVycm9ySGFuZGxlclBhcmFtXHJcblx0dG9rZW5BY2Nlc3NGdW5jdGlvbiA9IHRva2VuQWNjZXNzRnVuY3Rpb25QYXJhbVxyXG5cdGxvZ2dlciA9IGdldExvZ2dlcigpXHJcblxyXG5cdHlpZWxkIHRha2VFdmVyeShNT0RFTF9GRVRDSF9SRVFVRVNUX0FDVElPTl9UWVBFLkZFVENIX1JFUVVFU1QsIG1vZGVsRmV0Y2gpXHJcblx0eWllbGQgdGFrZUV2ZXJ5KE1PREVMX0ZFVENIX1JFUVVFU1RfQUNUSU9OX1RZUEUuUEVSSU9ESUNfRkVUQ0hfUkVRVUVTVCwgbW9kZWxGZXRjaFJlY3VycmluZylcclxufVxyXG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7O0FBQ0EsSUFBQUEsT0FBQSxHQUFBQyxPQUFBO0FBQ0EsSUFBQUMsZUFBQSxHQUFBQyxzQkFBQSxDQUFBRixPQUFBO0FBRUEsSUFBQUcsUUFBQSxHQUFBSCxPQUFBO0FBQ0EsSUFBQUksZUFBQSxHQUFBSixPQUFBO0FBQ0EsSUFBQUssWUFBQSxHQUFBTCxPQUFBO0FBQ0EsSUFBQU0sYUFBQSxHQUFBTixPQUFBO0FBQ0EsSUFBQU8sY0FBQSxHQUFBUCxPQUFBO0FBQ0EsSUFBQVEsTUFBQSxHQUFBUixPQUFBO0FBWUEsSUFBQVMsTUFBQSxHQUFBVCxPQUFBO0FBQ0EsSUFBQVUsTUFBQSxHQUFB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
|