@khanacademy/wonder-blocks-data 3.0.0 → 3.1.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/CHANGELOG.md +28 -2
- package/dist/es/index.js +204 -31
- package/dist/index.js +315 -70
- package/package.json +4 -3
- package/src/components/__tests__/gql-router.test.js +64 -0
- package/src/components/gql-router.js +66 -0
- package/src/hooks/__tests__/use-data.test.js +142 -106
- package/src/hooks/__tests__/use-gql.test.js +233 -0
- package/src/hooks/use-data.js +28 -23
- package/src/hooks/use-gql.js +77 -0
- package/src/index.js +12 -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 +65 -0
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
|
|
|
@@ -424,25 +462,49 @@ const useData = (handler, options) => {
|
|
|
424
462
|
// this will have cached data in those cases as it will be present on the
|
|
425
463
|
// initial render - and subsequent renders on the client it will be null.
|
|
426
464
|
const cachedResult = _util_response_cache_js__WEBPACK_IMPORTED_MODULE_6__[/* ResponseCache */ "a"].Default.getEntry(handler, options);
|
|
427
|
-
const [result, setResult] = Object(react__WEBPACK_IMPORTED_MODULE_1__["useState"])(cachedResult); //
|
|
465
|
+
const [result, setResult] = Object(react__WEBPACK_IMPORTED_MODULE_1__["useState"])(cachedResult); // Lookup to see if there's an interceptor for the handler.
|
|
466
|
+
// If we have one, we need to replace the handler with one that
|
|
467
|
+
// uses the interceptor.
|
|
468
|
+
|
|
469
|
+
const interceptorMap = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"]);
|
|
470
|
+
const interceptor = interceptorMap[handler.type]; // If we have an interceptor, we need to replace the handler with one that
|
|
471
|
+
// uses the interceptor. This helper function generates a new handler.
|
|
472
|
+
// We need this before we track the request as we want the interceptor
|
|
473
|
+
// to also work for tracked requests to simplify testing the server-side
|
|
474
|
+
// request fulfillment.
|
|
475
|
+
|
|
476
|
+
const getMaybeInterceptedHandler = () => {
|
|
477
|
+
if (interceptor == null) {
|
|
478
|
+
return handler;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const fulfillRequestFn = options => {
|
|
482
|
+
var _interceptor$fulfillR;
|
|
483
|
+
|
|
484
|
+
return (_interceptor$fulfillR = interceptor.fulfillRequest(options)) != null ? _interceptor$fulfillR : handler.fulfillRequest(options);
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
fulfillRequest: fulfillRequestFn,
|
|
489
|
+
getKey: options => handler.getKey(options),
|
|
490
|
+
type: handler.type,
|
|
491
|
+
hydrate: handler.hydrate
|
|
492
|
+
};
|
|
493
|
+
}; // We only track data requests when we are server-side and we don't
|
|
428
494
|
// already have a result, as given by the cachedData (which is also the
|
|
429
495
|
// initial value for the result state).
|
|
430
496
|
|
|
497
|
+
|
|
431
498
|
const maybeTrack = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_util_request_tracking_js__WEBPACK_IMPORTED_MODULE_4__[/* TrackerContext */ "b"]);
|
|
432
499
|
|
|
433
500
|
if (result == null && _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
|
|
434
|
-
maybeTrack == null ? void 0 : maybeTrack(
|
|
435
|
-
} //
|
|
436
|
-
// If we have one, we need to replace the handler with one that
|
|
437
|
-
// uses the interceptor.
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
const interceptorMap = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"]);
|
|
441
|
-
const interceptor = interceptorMap[handler.type]; // We need to update our request when the handler changes or the key
|
|
501
|
+
maybeTrack == null ? void 0 : maybeTrack(getMaybeInterceptedHandler(), options);
|
|
502
|
+
} // We need to update our request when the handler changes or the key
|
|
442
503
|
// to the options change, so we keep track of those.
|
|
443
504
|
// However, even if we are hydrating from cache, we still need to make the
|
|
444
505
|
// request at least once, so we do not initialize these references.
|
|
445
506
|
|
|
507
|
+
|
|
446
508
|
const handlerRef = Object(react__WEBPACK_IMPORTED_MODULE_1__["useRef"])();
|
|
447
509
|
const keyRef = Object(react__WEBPACK_IMPORTED_MODULE_1__["useRef"])();
|
|
448
510
|
const interceptorRef = Object(react__WEBPACK_IMPORTED_MODULE_1__["useRef"])(); // This effect will ensure that we fulfill the request as desired.
|
|
@@ -467,26 +529,7 @@ const useData = (handler, options) => {
|
|
|
467
529
|
if (cachedResult == null) {
|
|
468
530
|
// Mark ourselves as loading.
|
|
469
531
|
setResult(null);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
const getMaybeInterceptedHandler = () => {
|
|
473
|
-
if (interceptor == null) {
|
|
474
|
-
return handler;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const fulfillRequestFn = options => {
|
|
478
|
-
var _interceptor$fulfillR;
|
|
479
|
-
|
|
480
|
-
return (_interceptor$fulfillR = interceptor.fulfillRequest(options)) != null ? _interceptor$fulfillR : handler.fulfillRequest(options);
|
|
481
|
-
};
|
|
482
|
-
|
|
483
|
-
return {
|
|
484
|
-
fulfillRequest: fulfillRequestFn,
|
|
485
|
-
getKey: options => handler.getKey(options),
|
|
486
|
-
type: handler.type,
|
|
487
|
-
hydrate: handler.hydrate
|
|
488
|
-
};
|
|
489
|
-
}; // We aren't server-side, so let's make the request.
|
|
532
|
+
} // We aren't server-side, so let's make the request.
|
|
490
533
|
// The request handler is in control of whether that request actually
|
|
491
534
|
// happens or not.
|
|
492
535
|
|
|
@@ -529,12 +572,12 @@ const useData = (handler, options) => {
|
|
|
529
572
|
};
|
|
530
573
|
|
|
531
574
|
/***/ }),
|
|
532
|
-
/*
|
|
575
|
+
/* 7 */
|
|
533
576
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
534
577
|
|
|
535
578
|
"use strict";
|
|
536
579
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestFulfillment; });
|
|
537
|
-
/* 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);
|
|
538
581
|
|
|
539
582
|
|
|
540
583
|
let _default;
|
|
@@ -619,7 +662,18 @@ class RequestFulfillment {
|
|
|
619
662
|
}
|
|
620
663
|
|
|
621
664
|
/***/ }),
|
|
622
|
-
/*
|
|
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 */
|
|
623
677
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
624
678
|
|
|
625
679
|
"use strict";
|
|
@@ -768,7 +822,13 @@ class MemoryCache {
|
|
|
768
822
|
}
|
|
769
823
|
|
|
770
824
|
/***/ }),
|
|
771
|
-
/*
|
|
825
|
+
/* 10 */
|
|
826
|
+
/***/ (function(module, exports) {
|
|
827
|
+
|
|
828
|
+
module.exports = require("@khanacademy/wonder-stuff-core");
|
|
829
|
+
|
|
830
|
+
/***/ }),
|
|
831
|
+
/* 11 */
|
|
772
832
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
773
833
|
|
|
774
834
|
"use strict";
|
|
@@ -808,16 +868,16 @@ class RequestHandler {
|
|
|
808
868
|
}
|
|
809
869
|
|
|
810
870
|
/***/ }),
|
|
811
|
-
/*
|
|
871
|
+
/* 12 */
|
|
812
872
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
813
873
|
|
|
814
874
|
"use strict";
|
|
815
875
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return TrackData; });
|
|
816
876
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
817
877
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
818
|
-
/* 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);
|
|
819
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__);
|
|
820
|
-
/* 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);
|
|
821
881
|
|
|
822
882
|
|
|
823
883
|
|
|
@@ -839,13 +899,13 @@ class TrackData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
839
899
|
}
|
|
840
900
|
|
|
841
901
|
/***/ }),
|
|
842
|
-
/*
|
|
902
|
+
/* 13 */
|
|
843
903
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
844
904
|
|
|
845
905
|
"use strict";
|
|
846
906
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
847
907
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
848
|
-
/* 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);
|
|
849
909
|
|
|
850
910
|
|
|
851
911
|
|
|
@@ -862,14 +922,14 @@ const Data = props => {
|
|
|
862
922
|
/* harmony default export */ __webpack_exports__["a"] = (Data);
|
|
863
923
|
|
|
864
924
|
/***/ }),
|
|
865
|
-
/*
|
|
925
|
+
/* 14 */
|
|
866
926
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
867
927
|
|
|
868
928
|
"use strict";
|
|
869
929
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return InterceptData; });
|
|
870
930
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
871
931
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
872
|
-
/* 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);
|
|
873
933
|
|
|
874
934
|
|
|
875
935
|
|
|
@@ -908,7 +968,115 @@ class InterceptData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
908
968
|
}
|
|
909
969
|
|
|
910
970
|
/***/ }),
|
|
911
|
-
/*
|
|
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
|
+
} // Need to make sure we pass other errors along.
|
|
1070
|
+
|
|
1071
|
+
|
|
1072
|
+
throw error;
|
|
1073
|
+
});
|
|
1074
|
+
}, [fetch, defaultContext]);
|
|
1075
|
+
return gqlFetch;
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1078
|
+
/***/ }),
|
|
1079
|
+
/* 17 */
|
|
912
1080
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
913
1081
|
|
|
914
1082
|
"use strict";
|
|
@@ -951,7 +1119,75 @@ const resultFromCacheEntry = cacheEntry => {
|
|
|
951
1119
|
};
|
|
952
1120
|
|
|
953
1121
|
/***/ }),
|
|
954
|
-
/*
|
|
1122
|
+
/* 18 */
|
|
1123
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1124
|
+
|
|
1125
|
+
"use strict";
|
|
1126
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getGqlDataFromResponse; });
|
|
1127
|
+
/* harmony import */ var _gql_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* Validate a GQL operation response and extract the data.
|
|
1131
|
+
*/
|
|
1132
|
+
|
|
1133
|
+
const getGqlDataFromResponse = async response => {
|
|
1134
|
+
// Get the response as text, that way we can use the text in error
|
|
1135
|
+
// messaging, should our parsing fail.
|
|
1136
|
+
const bodyText = await response.text();
|
|
1137
|
+
let result;
|
|
1138
|
+
|
|
1139
|
+
try {
|
|
1140
|
+
result = JSON.parse(bodyText);
|
|
1141
|
+
} catch (e) {
|
|
1142
|
+
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, {
|
|
1143
|
+
metadata: {
|
|
1144
|
+
statusCode: response.status,
|
|
1145
|
+
bodyText
|
|
1146
|
+
},
|
|
1147
|
+
cause: e
|
|
1148
|
+
});
|
|
1149
|
+
} // Check for a bad status code.
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
if (response.status >= 300) {
|
|
1153
|
+
throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("Response unsuccessful", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].Network, {
|
|
1154
|
+
metadata: {
|
|
1155
|
+
statusCode: response.status,
|
|
1156
|
+
result
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
} // Check that we have a valid result payload.
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
if ( // Flow shouldn't be warning about this.
|
|
1163
|
+
// $FlowIgnore[method-unbinding]
|
|
1164
|
+
!Object.prototype.hasOwnProperty.call(result, "data") && // Flow shouldn't be warning about this.
|
|
1165
|
+
// $FlowIgnore[method-unbinding]
|
|
1166
|
+
!Object.prototype.hasOwnProperty.call(result, "errors")) {
|
|
1167
|
+
throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("Server response missing", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].BadResponse, {
|
|
1168
|
+
metadata: {
|
|
1169
|
+
statusCode: response.status,
|
|
1170
|
+
result
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
} // If the response payload has errors, throw an error.
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
if (result.errors != null && Array.isArray(result.errors) && result.errors.length > 0) {
|
|
1177
|
+
throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("GraphQL errors", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].ErrorResult, {
|
|
1178
|
+
metadata: {
|
|
1179
|
+
statusCode: response.status,
|
|
1180
|
+
result
|
|
1181
|
+
}
|
|
1182
|
+
});
|
|
1183
|
+
} // We got here, so return the data.
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
return result.data;
|
|
1187
|
+
};
|
|
1188
|
+
|
|
1189
|
+
/***/ }),
|
|
1190
|
+
/* 19 */
|
|
955
1191
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
956
1192
|
|
|
957
1193
|
"use strict";
|
|
@@ -961,25 +1197,36 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
961
1197
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasUnfulfilledRequests", function() { return hasUnfulfilledRequests; });
|
|
962
1198
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeFromCache", function() { return removeFromCache; });
|
|
963
1199
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeAllFromCache", function() { return removeAllFromCache; });
|
|
964
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
1200
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
965
1201
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
966
|
-
/* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
967
|
-
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
968
|
-
/* harmony import */ var _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(
|
|
1202
|
+
/* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
|
|
1203
|
+
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
|
|
1204
|
+
/* harmony import */ var _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11);
|
|
969
1205
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RequestHandler", function() { return _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__["a"]; });
|
|
970
1206
|
|
|
971
|
-
/* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(
|
|
1207
|
+
/* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(12);
|
|
972
1208
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TrackData", function() { return _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__["a"]; });
|
|
973
1209
|
|
|
974
|
-
/* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(
|
|
1210
|
+
/* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(13);
|
|
975
1211
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Data", function() { return _components_data_js__WEBPACK_IMPORTED_MODULE_5__["a"]; });
|
|
976
1212
|
|
|
977
|
-
/* harmony import */ var _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(
|
|
1213
|
+
/* harmony import */ var _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(14);
|
|
978
1214
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "InterceptData", function() { return _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__["a"]; });
|
|
979
1215
|
|
|
980
|
-
/* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(
|
|
1216
|
+
/* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(6);
|
|
981
1217
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useData", function() { return _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__["a"]; });
|
|
982
1218
|
|
|
1219
|
+
/* harmony import */ var _components_gql_router_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(15);
|
|
1220
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlRouter", function() { return _components_gql_router_js__WEBPACK_IMPORTED_MODULE_8__["a"]; });
|
|
1221
|
+
|
|
1222
|
+
/* harmony import */ var _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(16);
|
|
1223
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useGql", function() { return _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_9__["a"]; });
|
|
1224
|
+
|
|
1225
|
+
/* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(1);
|
|
1226
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlErrors", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__["b"]; });
|
|
1227
|
+
|
|
1228
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlError", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__["a"]; });
|
|
1229
|
+
|
|
983
1230
|
|
|
984
1231
|
|
|
985
1232
|
|
|
@@ -1000,15 +1247,13 @@ const hasUnfulfilledRequests = () => {
|
|
|
1000
1247
|
};
|
|
1001
1248
|
const removeFromCache = (handler, options) => _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ResponseCache */ "a"].Default.remove(handler, options);
|
|
1002
1249
|
const removeAllFromCache = (handler, predicate) => _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ResponseCache */ "a"].Default.removeAll(handler, predicate);
|
|
1003
|
-
/**
|
|
1004
|
-
* TODO(somewhatabstract): Export each cache type we implement.
|
|
1005
|
-
*
|
|
1006
|
-
* Is there a base type we export, like we do for RequestHandler?
|
|
1007
|
-
*/
|
|
1008
1250
|
|
|
1009
1251
|
|
|
1010
1252
|
|
|
1011
1253
|
|
|
1254
|
+
// GraphQL
|
|
1255
|
+
|
|
1256
|
+
|
|
1012
1257
|
|
|
1013
1258
|
|
|
1014
1259
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-data",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.2",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -14,13 +14,14 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@babel/runtime": "^7.16.3",
|
|
17
|
-
"@khanacademy/wonder-blocks-core": "^4.
|
|
17
|
+
"@khanacademy/wonder-blocks-core": "^4.2.1"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
+
"@khanacademy/wonder-stuff-core": "^0.1.2",
|
|
20
21
|
"react": "16.14.0"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
|
-
"wb-dev-build-settings": "^0.
|
|
24
|
+
"wb-dev-build-settings": "^0.3.0"
|
|
24
25
|
},
|
|
25
26
|
"author": "",
|
|
26
27
|
"license": "MIT"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {render} from "@testing-library/react";
|
|
4
|
+
|
|
5
|
+
import {GqlRouterContext} from "../../util/gql-router-context.js";
|
|
6
|
+
import {GqlRouter} from "../gql-router.js";
|
|
7
|
+
|
|
8
|
+
describe("GqlRouter", () => {
|
|
9
|
+
it("should provide the GqlRouterContext as configured", async () => {
|
|
10
|
+
// Arrange
|
|
11
|
+
const defaultContext = {
|
|
12
|
+
foo: "bar",
|
|
13
|
+
};
|
|
14
|
+
const fetch = jest.fn();
|
|
15
|
+
const CaptureContext = ({captureFn}) => {
|
|
16
|
+
captureFn(React.useContext(GqlRouterContext));
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Act
|
|
21
|
+
const result = await new Promise((resolve, reject) => {
|
|
22
|
+
render(
|
|
23
|
+
<GqlRouter defaultContext={defaultContext} fetch={fetch}>
|
|
24
|
+
<CaptureContext captureFn={resolve} />
|
|
25
|
+
</GqlRouter>,
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Assert
|
|
30
|
+
expect(result).toStrictEqual({
|
|
31
|
+
defaultContext,
|
|
32
|
+
fetch,
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should not render React.memo-ized children if props remain the same", () => {
|
|
37
|
+
// Arrange
|
|
38
|
+
const defaultContext = {
|
|
39
|
+
foo: "bar",
|
|
40
|
+
};
|
|
41
|
+
const fetch = jest.fn();
|
|
42
|
+
let renderCount = 0;
|
|
43
|
+
const Child = React.memo(() => {
|
|
44
|
+
const context = React.useContext(GqlRouterContext);
|
|
45
|
+
renderCount++;
|
|
46
|
+
return <div>{JSON.stringify(context)}</div>;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Act
|
|
50
|
+
const {rerender} = render(
|
|
51
|
+
<GqlRouter defaultContext={defaultContext} fetch={fetch}>
|
|
52
|
+
<Child />
|
|
53
|
+
</GqlRouter>,
|
|
54
|
+
);
|
|
55
|
+
rerender(
|
|
56
|
+
<GqlRouter defaultContext={defaultContext} fetch={fetch}>
|
|
57
|
+
<Child />
|
|
58
|
+
</GqlRouter>,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Assert
|
|
62
|
+
expect(renderCount).toBe(1);
|
|
63
|
+
});
|
|
64
|
+
});
|