@khanacademy/wonder-blocks-data 9.0.0 → 9.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 +6 -0
- package/dist/es/index.js +19 -6
- package/dist/index.js +51 -6
- package/package.json +1 -1
- package/src/util/__tests__/get-gql-request-id.test.js +33 -0
- package/src/util/get-gql-request-id.js +50 -6
package/CHANGELOG.md
CHANGED
package/dist/es/index.js
CHANGED
|
@@ -683,14 +683,27 @@ const InterceptRequests = ({
|
|
|
683
683
|
}, children);
|
|
684
684
|
};
|
|
685
685
|
|
|
686
|
-
const toString =
|
|
686
|
+
const toString = value => {
|
|
687
687
|
var _JSON$stringify;
|
|
688
688
|
|
|
689
|
-
if (typeof
|
|
690
|
-
return
|
|
689
|
+
if (typeof value === "string") {
|
|
690
|
+
return value;
|
|
691
691
|
}
|
|
692
692
|
|
|
693
|
-
return (_JSON$stringify = JSON.stringify(
|
|
693
|
+
return (_JSON$stringify = JSON.stringify(value)) != null ? _JSON$stringify : "";
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
const toStringifiedVariables = (acc, key, value) => {
|
|
697
|
+
if (typeof value === "object" && value !== null) {
|
|
698
|
+
return Object.entries(value).reduce((innerAcc, [i, v]) => {
|
|
699
|
+
const subKey = `${key}.${i}`;
|
|
700
|
+
return toStringifiedVariables(innerAcc, subKey, v);
|
|
701
|
+
}, acc);
|
|
702
|
+
} else {
|
|
703
|
+
acc[key] = toString(value);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
return acc;
|
|
694
707
|
};
|
|
695
708
|
|
|
696
709
|
const getGqlRequestId = (operation, variables, context) => {
|
|
@@ -702,8 +715,8 @@ const getGqlRequestId = (operation, variables, context) => {
|
|
|
702
715
|
|
|
703
716
|
if (variables != null) {
|
|
704
717
|
const stringifiedVariables = Object.keys(variables).reduce((acc, key) => {
|
|
705
|
-
|
|
706
|
-
return acc;
|
|
718
|
+
const value = variables[key];
|
|
719
|
+
return toStringifiedVariables(acc, key, value);
|
|
707
720
|
}, {});
|
|
708
721
|
const sortableVariables = new URLSearchParams(stringifiedVariables);
|
|
709
722
|
sortableVariables.sort();
|
package/dist/index.js
CHANGED
|
@@ -1843,14 +1843,32 @@ const InterceptRequests = ({
|
|
|
1843
1843
|
|
|
1844
1844
|
"use strict";
|
|
1845
1845
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getGqlRequestId; });
|
|
1846
|
-
const toString =
|
|
1846
|
+
const toString = value => {
|
|
1847
1847
|
var _JSON$stringify;
|
|
1848
1848
|
|
|
1849
|
-
if (typeof
|
|
1850
|
-
return
|
|
1849
|
+
if (typeof value === "string") {
|
|
1850
|
+
return value;
|
|
1851
1851
|
}
|
|
1852
1852
|
|
|
1853
|
-
return (_JSON$stringify = JSON.stringify(
|
|
1853
|
+
return (_JSON$stringify = JSON.stringify(value)) != null ? _JSON$stringify : "";
|
|
1854
|
+
};
|
|
1855
|
+
|
|
1856
|
+
const toStringifiedVariables = (acc, key, value) => {
|
|
1857
|
+
if (typeof value === "object" && value !== null) {
|
|
1858
|
+
// If we have an object or array, we build sub-variables so that
|
|
1859
|
+
// the ID is easily human-readable rather than having lots of
|
|
1860
|
+
// extra %-encodings. This means that an object or array variable
|
|
1861
|
+
// turns into x variables, where x is the field or element count of
|
|
1862
|
+
// variable. See below for example.
|
|
1863
|
+
return Object.entries(value).reduce((innerAcc, [i, v]) => {
|
|
1864
|
+
const subKey = `${key}.${i}`;
|
|
1865
|
+
return toStringifiedVariables(innerAcc, subKey, v);
|
|
1866
|
+
}, acc);
|
|
1867
|
+
} else {
|
|
1868
|
+
acc[key] = toString(value);
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
return acc;
|
|
1854
1872
|
};
|
|
1855
1873
|
/**
|
|
1856
1874
|
* Get an identifier for a given request.
|
|
@@ -1871,9 +1889,36 @@ const getGqlRequestId = (operation, variables, context) => {
|
|
|
1871
1889
|
|
|
1872
1890
|
if (variables != null) {
|
|
1873
1891
|
// We need to turn each variable into a string.
|
|
1892
|
+
// We also need to ensure we sort any sub-object keys.
|
|
1893
|
+
// `toStringifiedVariables` helps us with this by hoisting nested
|
|
1894
|
+
// data to individual variables for the purposes of ID generation.
|
|
1895
|
+
//
|
|
1896
|
+
// For example, consider variables:
|
|
1897
|
+
// {x: [1,2,3], y: {a: 1, b: 2, c: 3}, z: 123}
|
|
1898
|
+
//
|
|
1899
|
+
// Each variable, x, y and z, would be stringified into
|
|
1900
|
+
// stringifiedVariables as follows:
|
|
1901
|
+
// x becomes {"x.0": "1", "x.1": "2", "x.2": "3"}
|
|
1902
|
+
// y becomes {"y.a": "1", "y.b": "2", "y.c": "3"}
|
|
1903
|
+
// z becomes {"z": "123"}
|
|
1904
|
+
//
|
|
1905
|
+
// This then leads to stringifiedVariables being:
|
|
1906
|
+
// {
|
|
1907
|
+
// "x.0": "1",
|
|
1908
|
+
// "x.1": "2",
|
|
1909
|
+
// "x.2": "3",
|
|
1910
|
+
// "y.a": "1",
|
|
1911
|
+
// "y.b": "2",
|
|
1912
|
+
// "y.c": "3",
|
|
1913
|
+
// "z": "123",
|
|
1914
|
+
// }
|
|
1915
|
+
//
|
|
1916
|
+
// Thus allowing our use of URLSearchParams to both sort and easily
|
|
1917
|
+
// encode the variables into an idempotent identifier for those
|
|
1918
|
+
// variable values that is also human-readable.
|
|
1874
1919
|
const stringifiedVariables = Object.keys(variables).reduce((acc, key) => {
|
|
1875
|
-
|
|
1876
|
-
return acc;
|
|
1920
|
+
const value = variables[key];
|
|
1921
|
+
return toStringifiedVariables(acc, key, value);
|
|
1877
1922
|
}, {}); // We use the same mechanism as context to sort and arrange the
|
|
1878
1923
|
// variables.
|
|
1879
1924
|
|
package/package.json
CHANGED
|
@@ -71,4 +71,37 @@ describe("#getGqlRequestId", () => {
|
|
|
71
71
|
`variable1=value1&variable2=42&variable3=&variable4=null&variable5=true`,
|
|
72
72
|
);
|
|
73
73
|
});
|
|
74
|
+
|
|
75
|
+
it("should sort nested variable properties", () => {
|
|
76
|
+
// Arrange
|
|
77
|
+
const operation = {
|
|
78
|
+
type: "query",
|
|
79
|
+
id: "myQuery",
|
|
80
|
+
};
|
|
81
|
+
const variables = {
|
|
82
|
+
variable4: null,
|
|
83
|
+
variable2: 42,
|
|
84
|
+
variable1: "value1",
|
|
85
|
+
variable5: true,
|
|
86
|
+
variable3: undefined,
|
|
87
|
+
variable6: {
|
|
88
|
+
nested2: "nested2",
|
|
89
|
+
nested1: "nested1",
|
|
90
|
+
},
|
|
91
|
+
variable7: [1, 2, 3],
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Act
|
|
95
|
+
const requestId = getGqlRequestId(operation, variables, {
|
|
96
|
+
module: "MODULE",
|
|
97
|
+
curriculum: "CURRICULUM",
|
|
98
|
+
targetLocale: "LOCALE",
|
|
99
|
+
});
|
|
100
|
+
const result = new Set(requestId.split("|"));
|
|
101
|
+
|
|
102
|
+
// Assert
|
|
103
|
+
expect(result).toContain(
|
|
104
|
+
`variable1=value1&variable2=42&variable3=&variable4=null&variable5=true&variable6.nested1=nested1&variable6.nested2=nested2&variable7.0=1&variable7.1=2&variable7.2=3`,
|
|
105
|
+
);
|
|
106
|
+
});
|
|
74
107
|
});
|
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import type {GqlOperation, GqlContext} from "./gql-types.js";
|
|
3
3
|
|
|
4
|
-
const toString = (
|
|
5
|
-
if (typeof
|
|
6
|
-
return
|
|
4
|
+
const toString = (value: mixed): string => {
|
|
5
|
+
if (typeof value === "string") {
|
|
6
|
+
return value;
|
|
7
7
|
}
|
|
8
|
-
return JSON.stringify(
|
|
8
|
+
return JSON.stringify(value) ?? "";
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const toStringifiedVariables = (acc: any, key: string, value: mixed): any => {
|
|
12
|
+
if (typeof value === "object" && value !== null) {
|
|
13
|
+
// If we have an object or array, we build sub-variables so that
|
|
14
|
+
// the ID is easily human-readable rather than having lots of
|
|
15
|
+
// extra %-encodings. This means that an object or array variable
|
|
16
|
+
// turns into x variables, where x is the field or element count of
|
|
17
|
+
// variable. See below for example.
|
|
18
|
+
return Object.entries(value).reduce((innerAcc, [i, v]) => {
|
|
19
|
+
const subKey = `${key}.${i}`;
|
|
20
|
+
return toStringifiedVariables(innerAcc, subKey, v);
|
|
21
|
+
}, acc);
|
|
22
|
+
} else {
|
|
23
|
+
acc[key] = toString(value);
|
|
24
|
+
}
|
|
25
|
+
return acc;
|
|
9
26
|
};
|
|
10
27
|
|
|
11
28
|
/**
|
|
@@ -32,10 +49,37 @@ export const getGqlRequestId = <TData, TVariables: {...}>(
|
|
|
32
49
|
// Finally, if we have variables, we add those too.
|
|
33
50
|
if (variables != null) {
|
|
34
51
|
// We need to turn each variable into a string.
|
|
52
|
+
// We also need to ensure we sort any sub-object keys.
|
|
53
|
+
// `toStringifiedVariables` helps us with this by hoisting nested
|
|
54
|
+
// data to individual variables for the purposes of ID generation.
|
|
55
|
+
//
|
|
56
|
+
// For example, consider variables:
|
|
57
|
+
// {x: [1,2,3], y: {a: 1, b: 2, c: 3}, z: 123}
|
|
58
|
+
//
|
|
59
|
+
// Each variable, x, y and z, would be stringified into
|
|
60
|
+
// stringifiedVariables as follows:
|
|
61
|
+
// x becomes {"x.0": "1", "x.1": "2", "x.2": "3"}
|
|
62
|
+
// y becomes {"y.a": "1", "y.b": "2", "y.c": "3"}
|
|
63
|
+
// z becomes {"z": "123"}
|
|
64
|
+
//
|
|
65
|
+
// This then leads to stringifiedVariables being:
|
|
66
|
+
// {
|
|
67
|
+
// "x.0": "1",
|
|
68
|
+
// "x.1": "2",
|
|
69
|
+
// "x.2": "3",
|
|
70
|
+
// "y.a": "1",
|
|
71
|
+
// "y.b": "2",
|
|
72
|
+
// "y.c": "3",
|
|
73
|
+
// "z": "123",
|
|
74
|
+
// }
|
|
75
|
+
//
|
|
76
|
+
// Thus allowing our use of URLSearchParams to both sort and easily
|
|
77
|
+
// encode the variables into an idempotent identifier for those
|
|
78
|
+
// variable values that is also human-readable.
|
|
35
79
|
const stringifiedVariables = Object.keys(variables).reduce(
|
|
36
80
|
(acc, key) => {
|
|
37
|
-
|
|
38
|
-
return acc;
|
|
81
|
+
const value = variables[key];
|
|
82
|
+
return toStringifiedVariables(acc, key, value);
|
|
39
83
|
},
|
|
40
84
|
{},
|
|
41
85
|
);
|