@khanacademy/wonder-blocks-data 3.0.1 → 3.1.0
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/CHANGELOG.md +7 -0
- package/dist/es/index.js +167 -2
- package/dist/index.js +278 -41
- package/package.json +2 -1
- package/src/components/__tests__/gql-router.test.js +64 -0
- package/src/components/gql-router.js +66 -0
- package/src/hooks/__tests__/use-gql.test.js +233 -0
- package/src/hooks/use-gql.js +75 -0
- package/src/index.js +6 -6
- package/src/util/__tests__/get-gql-data-from-response.test.js +187 -0
- package/src/util/get-gql-data-from-response.js +69 -0
- package/src/util/gql-error.js +36 -0
- package/src/util/gql-router-context.js +6 -0
- package/src/util/gql-types.js +60 -0
package/CHANGELOG.md
CHANGED
package/dist/es/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Server } from '@khanacademy/wonder-blocks-core';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { useState, useContext, useRef, useEffect } from 'react';
|
|
3
|
+
import { useState, useContext, useRef, useEffect, useMemo } from 'react';
|
|
4
4
|
import _extends from '@babel/runtime/helpers/extends';
|
|
5
|
+
import { Errors, KindError } from '@khanacademy/wonder-stuff-core';
|
|
5
6
|
|
|
6
7
|
function deepClone(source) {
|
|
7
8
|
/**
|
|
@@ -738,6 +739,170 @@ class InterceptData extends React.Component {
|
|
|
738
739
|
|
|
739
740
|
}
|
|
740
741
|
|
|
742
|
+
const GqlRouterContext = /*#__PURE__*/React.createContext(null);
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Configure GraphQL routing for GraphQL hooks and components.
|
|
746
|
+
*
|
|
747
|
+
* These can be nested. Components and hooks relying on the GraphQL routing
|
|
748
|
+
* will use the configuration from their closest ancestral GqlRouter.
|
|
749
|
+
*/
|
|
750
|
+
const GqlRouter = ({
|
|
751
|
+
defaultContext: thisDefaultContext,
|
|
752
|
+
fetch: thisFetch,
|
|
753
|
+
children
|
|
754
|
+
}) => {
|
|
755
|
+
// We don't care if we're nested. We always force our callers to define
|
|
756
|
+
// everything. It makes for a clearer API and requires less error checking
|
|
757
|
+
// code (assuming our flow types are correct). We also don't default fetch
|
|
758
|
+
// to anything - our callers can tell us what function to use quite easily.
|
|
759
|
+
// If code that consumes this wants more nuanced nesting, it can implement
|
|
760
|
+
// it within its own GqlRouter than then defers to this one.
|
|
761
|
+
// We want to always use the same object if things haven't changed to avoid
|
|
762
|
+
// over-rendering consumers of our context, let's memoize the configuration.
|
|
763
|
+
// By doing this, if a component under children that uses this context
|
|
764
|
+
// uses React.memo, we won't force it to re-render every time we render
|
|
765
|
+
// because we'll only change the context value if something has actually
|
|
766
|
+
// changed.
|
|
767
|
+
const configuration = React.useMemo(() => ({
|
|
768
|
+
fetch: thisFetch,
|
|
769
|
+
defaultContext: thisDefaultContext
|
|
770
|
+
}), [thisDefaultContext, thisFetch]);
|
|
771
|
+
return /*#__PURE__*/React.createElement(GqlRouterContext.Provider, {
|
|
772
|
+
value: configuration
|
|
773
|
+
}, children);
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Error kinds for GqlError.
|
|
778
|
+
*/
|
|
779
|
+
const GqlErrors = Object.freeze(_extends({}, Errors, {
|
|
780
|
+
Network: "Network",
|
|
781
|
+
Parse: "Parse",
|
|
782
|
+
BadResponse: "BadResponse",
|
|
783
|
+
ErrorResult: "ErrorResult"
|
|
784
|
+
}));
|
|
785
|
+
/**
|
|
786
|
+
* An error from the GQL API.
|
|
787
|
+
*/
|
|
788
|
+
|
|
789
|
+
class GqlError extends KindError {
|
|
790
|
+
constructor(message, kind, {
|
|
791
|
+
metadata,
|
|
792
|
+
cause
|
|
793
|
+
} = {}) {
|
|
794
|
+
super(message, kind, {
|
|
795
|
+
metadata,
|
|
796
|
+
cause,
|
|
797
|
+
prefix: "Gql"
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Validate a GQL operation response and extract the data.
|
|
805
|
+
*/
|
|
806
|
+
|
|
807
|
+
const getGqlDataFromResponse = async response => {
|
|
808
|
+
// Get the response as text, that way we can use the text in error
|
|
809
|
+
// messaging, should our parsing fail.
|
|
810
|
+
const bodyText = await response.text();
|
|
811
|
+
let result;
|
|
812
|
+
|
|
813
|
+
try {
|
|
814
|
+
result = JSON.parse(bodyText);
|
|
815
|
+
} catch (e) {
|
|
816
|
+
throw new GqlError("Failed to parse response", GqlErrors.Parse, {
|
|
817
|
+
metadata: {
|
|
818
|
+
statusCode: response.status,
|
|
819
|
+
bodyText
|
|
820
|
+
},
|
|
821
|
+
cause: e
|
|
822
|
+
});
|
|
823
|
+
} // Check for a bad status code.
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
if (response.status >= 300) {
|
|
827
|
+
throw new GqlError("Response unsuccessful", GqlErrors.Network, {
|
|
828
|
+
metadata: {
|
|
829
|
+
statusCode: response.status,
|
|
830
|
+
result
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
} // Check that we have a valid result payload.
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
if ( // Flow shouldn't be warning about this.
|
|
837
|
+
// $FlowIgnore[method-unbinding]
|
|
838
|
+
!Object.prototype.hasOwnProperty.call(result, "data") && // Flow shouldn't be warning about this.
|
|
839
|
+
// $FlowIgnore[method-unbinding]
|
|
840
|
+
!Object.prototype.hasOwnProperty.call(result, "errors")) {
|
|
841
|
+
throw new GqlError("Server response missing", GqlErrors.BadResponse, {
|
|
842
|
+
metadata: {
|
|
843
|
+
statusCode: response.status,
|
|
844
|
+
result
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
} // If the response payload has errors, throw an error.
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
if (result.errors != null && Array.isArray(result.errors) && result.errors.length > 0) {
|
|
851
|
+
throw new GqlError("GraphQL errors", GqlErrors.ErrorResult, {
|
|
852
|
+
metadata: {
|
|
853
|
+
statusCode: response.status,
|
|
854
|
+
result
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
} // We got here, so return the data.
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
return result.data;
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Hook to obtain a gqlFetch function for performing GraphQL requests.
|
|
865
|
+
*
|
|
866
|
+
* The fetch function will resolve null if the request was aborted, otherwise
|
|
867
|
+
* it will resolve the data returned by the GraphQL server.
|
|
868
|
+
*/
|
|
869
|
+
const useGql = () => {
|
|
870
|
+
// This hook only works if the `GqlRouter` has been used to setup context.
|
|
871
|
+
const gqlRouterContext = useContext(GqlRouterContext);
|
|
872
|
+
|
|
873
|
+
if (gqlRouterContext == null) {
|
|
874
|
+
throw new GqlError("No GqlRouter", GqlErrors.Internal);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const {
|
|
878
|
+
fetch,
|
|
879
|
+
defaultContext
|
|
880
|
+
} = gqlRouterContext; // Let's memoize the gqlFetch function we create based off our context.
|
|
881
|
+
// That way, even if the context happens to change, if its values don't
|
|
882
|
+
// we give the same function instance back to our callers instead of
|
|
883
|
+
// making a new one. That then means they can safely use the return value
|
|
884
|
+
// in hooks deps without fear of it triggering extra renders.
|
|
885
|
+
|
|
886
|
+
const gqlFetch = useMemo(() => (operation, options = Object.freeze({})) => {
|
|
887
|
+
const {
|
|
888
|
+
variables,
|
|
889
|
+
context
|
|
890
|
+
} = options; // Invoke the fetch and extract the data.
|
|
891
|
+
|
|
892
|
+
return fetch(operation, variables, _extends({}, defaultContext, context)).then(getGqlDataFromResponse, error => {
|
|
893
|
+
// Return null if the request was aborted.
|
|
894
|
+
// The only way to detect this reliably, it seems, is to
|
|
895
|
+
// check the error name and see if it's "AbortError" (this
|
|
896
|
+
// is also what Apollo does).
|
|
897
|
+
// Even then, it's reliant on the fetch supporting aborts.
|
|
898
|
+
if (error.name === "AbortError") {
|
|
899
|
+
return null;
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
}, [fetch, defaultContext]);
|
|
903
|
+
return gqlFetch;
|
|
904
|
+
};
|
|
905
|
+
|
|
741
906
|
const initializeCache = source => ResponseCache.Default.initialize(source);
|
|
742
907
|
const fulfillAllDataRequests = () => {
|
|
743
908
|
if (!Server.isServerSide()) {
|
|
@@ -756,4 +921,4 @@ const hasUnfulfilledRequests = () => {
|
|
|
756
921
|
const removeFromCache = (handler, options) => ResponseCache.Default.remove(handler, options);
|
|
757
922
|
const removeAllFromCache = (handler, predicate) => ResponseCache.Default.removeAll(handler, predicate);
|
|
758
923
|
|
|
759
|
-
export { Data, InterceptData, RequestHandler, TrackData, fulfillAllDataRequests, hasUnfulfilledRequests, initializeCache, removeAllFromCache, removeFromCache, useData };
|
|
924
|
+
export { Data, GqlError, GqlErrors, GqlRouter, InterceptData, RequestHandler, TrackData, fulfillAllDataRequests, hasUnfulfilledRequests, initializeCache, removeAllFromCache, removeFromCache, useData, useGql };
|
package/dist/index.js
CHANGED
|
@@ -82,7 +82,7 @@ module.exports =
|
|
|
82
82
|
/******/
|
|
83
83
|
/******/
|
|
84
84
|
/******/ // Load entry module and return exports
|
|
85
|
-
/******/ return __webpack_require__(__webpack_require__.s =
|
|
85
|
+
/******/ return __webpack_require__(__webpack_require__.s = 19);
|
|
86
86
|
/******/ })
|
|
87
87
|
/************************************************************************/
|
|
88
88
|
/******/ ([
|
|
@@ -93,19 +93,57 @@ module.exports = require("react");
|
|
|
93
93
|
|
|
94
94
|
/***/ }),
|
|
95
95
|
/* 1 */
|
|
96
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
97
|
+
|
|
98
|
+
"use strict";
|
|
99
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return GqlErrors; });
|
|
100
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlError; });
|
|
101
|
+
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(10);
|
|
102
|
+
/* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Error kinds for GqlError.
|
|
107
|
+
*/
|
|
108
|
+
const GqlErrors = Object.freeze({ ..._khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["Errors"],
|
|
109
|
+
Network: "Network",
|
|
110
|
+
Parse: "Parse",
|
|
111
|
+
BadResponse: "BadResponse",
|
|
112
|
+
ErrorResult: "ErrorResult"
|
|
113
|
+
});
|
|
114
|
+
/**
|
|
115
|
+
* An error from the GQL API.
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
class GqlError extends _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["KindError"] {
|
|
119
|
+
constructor(message, kind, {
|
|
120
|
+
metadata,
|
|
121
|
+
cause
|
|
122
|
+
} = {}) {
|
|
123
|
+
super(message, kind, {
|
|
124
|
+
metadata,
|
|
125
|
+
cause,
|
|
126
|
+
prefix: "Gql"
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/***/ }),
|
|
133
|
+
/* 2 */
|
|
96
134
|
/***/ (function(module, exports) {
|
|
97
135
|
|
|
98
136
|
module.exports = require("@khanacademy/wonder-blocks-core");
|
|
99
137
|
|
|
100
138
|
/***/ }),
|
|
101
|
-
/*
|
|
139
|
+
/* 3 */
|
|
102
140
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
103
141
|
|
|
104
142
|
"use strict";
|
|
105
143
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return ResponseCache; });
|
|
106
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
144
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
107
145
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
108
|
-
/* harmony import */ var _memory_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
146
|
+
/* harmony import */ var _memory_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
|
|
109
147
|
|
|
110
148
|
|
|
111
149
|
|
|
@@ -227,7 +265,7 @@ class ResponseCache {
|
|
|
227
265
|
}
|
|
228
266
|
|
|
229
267
|
/***/ }),
|
|
230
|
-
/*
|
|
268
|
+
/* 4 */
|
|
231
269
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
232
270
|
|
|
233
271
|
"use strict";
|
|
@@ -235,8 +273,8 @@ class ResponseCache {
|
|
|
235
273
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestTracker; });
|
|
236
274
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
237
275
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
238
|
-
/* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
239
|
-
/* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
276
|
+
/* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
|
|
277
|
+
/* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
|
|
240
278
|
|
|
241
279
|
|
|
242
280
|
|
|
@@ -379,7 +417,7 @@ class RequestTracker {
|
|
|
379
417
|
}
|
|
380
418
|
|
|
381
419
|
/***/ }),
|
|
382
|
-
/*
|
|
420
|
+
/* 5 */
|
|
383
421
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
384
422
|
|
|
385
423
|
"use strict";
|
|
@@ -396,20 +434,20 @@ const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["create
|
|
|
396
434
|
/* harmony default export */ __webpack_exports__["a"] = (InterceptContext);
|
|
397
435
|
|
|
398
436
|
/***/ }),
|
|
399
|
-
/*
|
|
437
|
+
/* 6 */
|
|
400
438
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
401
439
|
|
|
402
440
|
"use strict";
|
|
403
441
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useData; });
|
|
404
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
442
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
405
443
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
406
444
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
|
|
407
445
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
|
|
408
|
-
/* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
409
|
-
/* harmony import */ var _components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(
|
|
410
|
-
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(
|
|
411
|
-
/* harmony import */ var _util_result_from_cache_entry_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(
|
|
412
|
-
/* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(
|
|
446
|
+
/* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
|
|
447
|
+
/* harmony import */ var _components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5);
|
|
448
|
+
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4);
|
|
449
|
+
/* harmony import */ var _util_result_from_cache_entry_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(17);
|
|
450
|
+
/* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3);
|
|
413
451
|
|
|
414
452
|
|
|
415
453
|
|
|
@@ -534,12 +572,12 @@ const useData = (handler, options) => {
|
|
|
534
572
|
};
|
|
535
573
|
|
|
536
574
|
/***/ }),
|
|
537
|
-
/*
|
|
575
|
+
/* 7 */
|
|
538
576
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
539
577
|
|
|
540
578
|
"use strict";
|
|
541
579
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestFulfillment; });
|
|
542
|
-
/* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
580
|
+
/* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
|
|
543
581
|
|
|
544
582
|
|
|
545
583
|
let _default;
|
|
@@ -624,7 +662,18 @@ class RequestFulfillment {
|
|
|
624
662
|
}
|
|
625
663
|
|
|
626
664
|
/***/ }),
|
|
627
|
-
/*
|
|
665
|
+
/* 8 */
|
|
666
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
667
|
+
|
|
668
|
+
"use strict";
|
|
669
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlRouterContext; });
|
|
670
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
671
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
672
|
+
|
|
673
|
+
const GqlRouterContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"](null);
|
|
674
|
+
|
|
675
|
+
/***/ }),
|
|
676
|
+
/* 9 */
|
|
628
677
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
629
678
|
|
|
630
679
|
"use strict";
|
|
@@ -773,7 +822,13 @@ class MemoryCache {
|
|
|
773
822
|
}
|
|
774
823
|
|
|
775
824
|
/***/ }),
|
|
776
|
-
/*
|
|
825
|
+
/* 10 */
|
|
826
|
+
/***/ (function(module, exports) {
|
|
827
|
+
|
|
828
|
+
module.exports = require("@khanacademy/wonder-stuff-core");
|
|
829
|
+
|
|
830
|
+
/***/ }),
|
|
831
|
+
/* 11 */
|
|
777
832
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
778
833
|
|
|
779
834
|
"use strict";
|
|
@@ -813,16 +868,16 @@ class RequestHandler {
|
|
|
813
868
|
}
|
|
814
869
|
|
|
815
870
|
/***/ }),
|
|
816
|
-
/*
|
|
871
|
+
/* 12 */
|
|
817
872
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
818
873
|
|
|
819
874
|
"use strict";
|
|
820
875
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return TrackData; });
|
|
821
876
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
822
877
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
823
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
878
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
|
|
824
879
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__);
|
|
825
|
-
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
880
|
+
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
|
|
826
881
|
|
|
827
882
|
|
|
828
883
|
|
|
@@ -844,13 +899,13 @@ class TrackData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
844
899
|
}
|
|
845
900
|
|
|
846
901
|
/***/ }),
|
|
847
|
-
/*
|
|
902
|
+
/* 13 */
|
|
848
903
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
849
904
|
|
|
850
905
|
"use strict";
|
|
851
906
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
852
907
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
853
|
-
/* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
908
|
+
/* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
|
|
854
909
|
|
|
855
910
|
|
|
856
911
|
|
|
@@ -867,14 +922,14 @@ const Data = props => {
|
|
|
867
922
|
/* harmony default export */ __webpack_exports__["a"] = (Data);
|
|
868
923
|
|
|
869
924
|
/***/ }),
|
|
870
|
-
/*
|
|
925
|
+
/* 14 */
|
|
871
926
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
872
927
|
|
|
873
928
|
"use strict";
|
|
874
929
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return InterceptData; });
|
|
875
930
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
876
931
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
877
|
-
/* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
932
|
+
/* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
878
933
|
|
|
879
934
|
|
|
880
935
|
|
|
@@ -913,7 +968,112 @@ class InterceptData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
913
968
|
}
|
|
914
969
|
|
|
915
970
|
/***/ }),
|
|
916
|
-
/*
|
|
971
|
+
/* 15 */
|
|
972
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
973
|
+
|
|
974
|
+
"use strict";
|
|
975
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlRouter; });
|
|
976
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
977
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
978
|
+
/* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Configure GraphQL routing for GraphQL hooks and components.
|
|
984
|
+
*
|
|
985
|
+
* These can be nested. Components and hooks relying on the GraphQL routing
|
|
986
|
+
* will use the configuration from their closest ancestral GqlRouter.
|
|
987
|
+
*/
|
|
988
|
+
const GqlRouter = ({
|
|
989
|
+
defaultContext: thisDefaultContext,
|
|
990
|
+
fetch: thisFetch,
|
|
991
|
+
children
|
|
992
|
+
}) => {
|
|
993
|
+
// We don't care if we're nested. We always force our callers to define
|
|
994
|
+
// everything. It makes for a clearer API and requires less error checking
|
|
995
|
+
// code (assuming our flow types are correct). We also don't default fetch
|
|
996
|
+
// to anything - our callers can tell us what function to use quite easily.
|
|
997
|
+
// If code that consumes this wants more nuanced nesting, it can implement
|
|
998
|
+
// it within its own GqlRouter than then defers to this one.
|
|
999
|
+
// We want to always use the same object if things haven't changed to avoid
|
|
1000
|
+
// over-rendering consumers of our context, let's memoize the configuration.
|
|
1001
|
+
// By doing this, if a component under children that uses this context
|
|
1002
|
+
// uses React.memo, we won't force it to re-render every time we render
|
|
1003
|
+
// because we'll only change the context value if something has actually
|
|
1004
|
+
// changed.
|
|
1005
|
+
const configuration = react__WEBPACK_IMPORTED_MODULE_0__["useMemo"](() => ({
|
|
1006
|
+
fetch: thisFetch,
|
|
1007
|
+
defaultContext: thisDefaultContext
|
|
1008
|
+
}), [thisDefaultContext, thisFetch]);
|
|
1009
|
+
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__[/* GqlRouterContext */ "a"].Provider, {
|
|
1010
|
+
value: configuration
|
|
1011
|
+
}, children);
|
|
1012
|
+
};
|
|
1013
|
+
|
|
1014
|
+
/***/ }),
|
|
1015
|
+
/* 16 */
|
|
1016
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1017
|
+
|
|
1018
|
+
"use strict";
|
|
1019
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useGql; });
|
|
1020
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
1021
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
1022
|
+
/* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
|
|
1023
|
+
/* harmony import */ var _util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(18);
|
|
1024
|
+
/* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1);
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* Hook to obtain a gqlFetch function for performing GraphQL requests.
|
|
1032
|
+
*
|
|
1033
|
+
* The fetch function will resolve null if the request was aborted, otherwise
|
|
1034
|
+
* it will resolve the data returned by the GraphQL server.
|
|
1035
|
+
*/
|
|
1036
|
+
const useGql = () => {
|
|
1037
|
+
// This hook only works if the `GqlRouter` has been used to setup context.
|
|
1038
|
+
const gqlRouterContext = Object(react__WEBPACK_IMPORTED_MODULE_0__["useContext"])(_util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__[/* GqlRouterContext */ "a"]);
|
|
1039
|
+
|
|
1040
|
+
if (gqlRouterContext == null) {
|
|
1041
|
+
throw new _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__[/* GqlError */ "a"]("No GqlRouter", _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__[/* GqlErrors */ "b"].Internal);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
const {
|
|
1045
|
+
fetch,
|
|
1046
|
+
defaultContext
|
|
1047
|
+
} = gqlRouterContext; // Let's memoize the gqlFetch function we create based off our context.
|
|
1048
|
+
// That way, even if the context happens to change, if its values don't
|
|
1049
|
+
// we give the same function instance back to our callers instead of
|
|
1050
|
+
// making a new one. That then means they can safely use the return value
|
|
1051
|
+
// in hooks deps without fear of it triggering extra renders.
|
|
1052
|
+
|
|
1053
|
+
const gqlFetch = Object(react__WEBPACK_IMPORTED_MODULE_0__["useMemo"])(() => (operation, options = Object.freeze({})) => {
|
|
1054
|
+
const {
|
|
1055
|
+
variables,
|
|
1056
|
+
context
|
|
1057
|
+
} = options; // Invoke the fetch and extract the data.
|
|
1058
|
+
|
|
1059
|
+
return fetch(operation, variables, { ...defaultContext,
|
|
1060
|
+
...context
|
|
1061
|
+
}).then(_util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_2__[/* getGqlDataFromResponse */ "a"], error => {
|
|
1062
|
+
// Return null if the request was aborted.
|
|
1063
|
+
// The only way to detect this reliably, it seems, is to
|
|
1064
|
+
// check the error name and see if it's "AbortError" (this
|
|
1065
|
+
// is also what Apollo does).
|
|
1066
|
+
// Even then, it's reliant on the fetch supporting aborts.
|
|
1067
|
+
if (error.name === "AbortError") {
|
|
1068
|
+
return null;
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
}, [fetch, defaultContext]);
|
|
1072
|
+
return gqlFetch;
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
/***/ }),
|
|
1076
|
+
/* 17 */
|
|
917
1077
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
918
1078
|
|
|
919
1079
|
"use strict";
|
|
@@ -956,7 +1116,75 @@ const resultFromCacheEntry = cacheEntry => {
|
|
|
956
1116
|
};
|
|
957
1117
|
|
|
958
1118
|
/***/ }),
|
|
959
|
-
/*
|
|
1119
|
+
/* 18 */
|
|
1120
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1121
|
+
|
|
1122
|
+
"use strict";
|
|
1123
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getGqlDataFromResponse; });
|
|
1124
|
+
/* harmony import */ var _gql_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* Validate a GQL operation response and extract the data.
|
|
1128
|
+
*/
|
|
1129
|
+
|
|
1130
|
+
const getGqlDataFromResponse = async response => {
|
|
1131
|
+
// Get the response as text, that way we can use the text in error
|
|
1132
|
+
// messaging, should our parsing fail.
|
|
1133
|
+
const bodyText = await response.text();
|
|
1134
|
+
let result;
|
|
1135
|
+
|
|
1136
|
+
try {
|
|
1137
|
+
result = JSON.parse(bodyText);
|
|
1138
|
+
} catch (e) {
|
|
1139
|
+
throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("Failed to parse response", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].Parse, {
|
|
1140
|
+
metadata: {
|
|
1141
|
+
statusCode: response.status,
|
|
1142
|
+
bodyText
|
|
1143
|
+
},
|
|
1144
|
+
cause: e
|
|
1145
|
+
});
|
|
1146
|
+
} // Check for a bad status code.
|
|
1147
|
+
|
|
1148
|
+
|
|
1149
|
+
if (response.status >= 300) {
|
|
1150
|
+
throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("Response unsuccessful", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].Network, {
|
|
1151
|
+
metadata: {
|
|
1152
|
+
statusCode: response.status,
|
|
1153
|
+
result
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
} // Check that we have a valid result payload.
|
|
1157
|
+
|
|
1158
|
+
|
|
1159
|
+
if ( // Flow shouldn't be warning about this.
|
|
1160
|
+
// $FlowIgnore[method-unbinding]
|
|
1161
|
+
!Object.prototype.hasOwnProperty.call(result, "data") && // Flow shouldn't be warning about this.
|
|
1162
|
+
// $FlowIgnore[method-unbinding]
|
|
1163
|
+
!Object.prototype.hasOwnProperty.call(result, "errors")) {
|
|
1164
|
+
throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("Server response missing", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].BadResponse, {
|
|
1165
|
+
metadata: {
|
|
1166
|
+
statusCode: response.status,
|
|
1167
|
+
result
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
} // If the response payload has errors, throw an error.
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
if (result.errors != null && Array.isArray(result.errors) && result.errors.length > 0) {
|
|
1174
|
+
throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("GraphQL errors", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].ErrorResult, {
|
|
1175
|
+
metadata: {
|
|
1176
|
+
statusCode: response.status,
|
|
1177
|
+
result
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
1180
|
+
} // We got here, so return the data.
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
return result.data;
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
/***/ }),
|
|
1187
|
+
/* 19 */
|
|
960
1188
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
961
1189
|
|
|
962
1190
|
"use strict";
|
|
@@ -966,25 +1194,36 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
966
1194
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasUnfulfilledRequests", function() { return hasUnfulfilledRequests; });
|
|
967
1195
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeFromCache", function() { return removeFromCache; });
|
|
968
1196
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeAllFromCache", function() { return removeAllFromCache; });
|
|
969
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
1197
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
970
1198
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
971
|
-
/* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
972
|
-
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
973
|
-
/* harmony import */ var _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(
|
|
1199
|
+
/* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
|
|
1200
|
+
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
|
|
1201
|
+
/* harmony import */ var _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11);
|
|
974
1202
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RequestHandler", function() { return _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__["a"]; });
|
|
975
1203
|
|
|
976
|
-
/* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(
|
|
1204
|
+
/* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(12);
|
|
977
1205
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TrackData", function() { return _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__["a"]; });
|
|
978
1206
|
|
|
979
|
-
/* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(
|
|
1207
|
+
/* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(13);
|
|
980
1208
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Data", function() { return _components_data_js__WEBPACK_IMPORTED_MODULE_5__["a"]; });
|
|
981
1209
|
|
|
982
|
-
/* harmony import */ var _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(
|
|
1210
|
+
/* harmony import */ var _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(14);
|
|
983
1211
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "InterceptData", function() { return _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__["a"]; });
|
|
984
1212
|
|
|
985
|
-
/* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(
|
|
1213
|
+
/* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(6);
|
|
986
1214
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useData", function() { return _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__["a"]; });
|
|
987
1215
|
|
|
1216
|
+
/* harmony import */ var _components_gql_router_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(15);
|
|
1217
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlRouter", function() { return _components_gql_router_js__WEBPACK_IMPORTED_MODULE_8__["a"]; });
|
|
1218
|
+
|
|
1219
|
+
/* harmony import */ var _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(16);
|
|
1220
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useGql", function() { return _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_9__["a"]; });
|
|
1221
|
+
|
|
1222
|
+
/* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(1);
|
|
1223
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlErrors", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__["b"]; });
|
|
1224
|
+
|
|
1225
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlError", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__["a"]; });
|
|
1226
|
+
|
|
988
1227
|
|
|
989
1228
|
|
|
990
1229
|
|
|
@@ -1005,15 +1244,13 @@ const hasUnfulfilledRequests = () => {
|
|
|
1005
1244
|
};
|
|
1006
1245
|
const removeFromCache = (handler, options) => _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ResponseCache */ "a"].Default.remove(handler, options);
|
|
1007
1246
|
const removeAllFromCache = (handler, predicate) => _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ResponseCache */ "a"].Default.removeAll(handler, predicate);
|
|
1008
|
-
/**
|
|
1009
|
-
* TODO(somewhatabstract): Export each cache type we implement.
|
|
1010
|
-
*
|
|
1011
|
-
* Is there a base type we export, like we do for RequestHandler?
|
|
1012
|
-
*/
|
|
1013
1247
|
|
|
1014
1248
|
|
|
1015
1249
|
|
|
1016
1250
|
|
|
1251
|
+
// GraphQL
|
|
1252
|
+
|
|
1253
|
+
|
|
1017
1254
|
|
|
1018
1255
|
|
|
1019
1256
|
|