@dhis2/app-service-data 3.4.0 → 3.4.3

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.
@@ -63,13 +63,26 @@ const isAction = resource => resource.startsWith(actionPrefix);
63
63
 
64
64
  const makeActionPath = resource => (0, _path.joinPath)('dhis-web-commons', "".concat(resource.substr(actionPrefix.length), ".action"));
65
65
 
66
- const queryToResourcePath = (apiPath, query, type) => {
66
+ const skipApiVersion = (resource, config) => {
67
+ if (resource === 'tracker' || resource.startsWith('tracker/')) {
68
+ var _config$serverVersion, _config$serverVersion2;
69
+
70
+ if (!((_config$serverVersion = config.serverVersion) !== null && _config$serverVersion !== void 0 && _config$serverVersion.minor) || ((_config$serverVersion2 = config.serverVersion) === null || _config$serverVersion2 === void 0 ? void 0 : _config$serverVersion2.minor) < 38) {
71
+ return true;
72
+ }
73
+ }
74
+
75
+ return false;
76
+ };
77
+
78
+ const queryToResourcePath = (link, query, type) => {
67
79
  const {
68
80
  resource,
69
81
  id,
70
82
  params = {}
71
83
  } = query;
72
- const base = isAction(resource) ? makeActionPath(resource) : (0, _path.joinPath)(apiPath, resource, id);
84
+ const apiBase = skipApiVersion(resource, link.config) ? link.unversionedApiPath : link.versionedApiPath;
85
+ const base = isAction(resource) ? makeActionPath(resource) : (0, _path.joinPath)(apiBase, resource, id);
73
86
  (0, _validateQuery.validateResourceQuery)(query, type);
74
87
 
75
88
  if (Object.keys(params).length) {
@@ -1,8 +1,22 @@
1
1
  "use strict";
2
2
 
3
+ var _RestAPILink = require("../RestAPILink");
4
+
3
5
  var _queryToResourcePath = require("./queryToResourcePath");
4
6
 
5
- const apiPath = '<api>';
7
+ const createLink = config => new _RestAPILink.RestAPILink(config);
8
+
9
+ const defaultConfig = {
10
+ basePath: '<base>',
11
+ apiVersion: '37',
12
+ serverVersion: {
13
+ major: 2,
14
+ minor: 37,
15
+ patch: 11
16
+ }
17
+ };
18
+ const link = createLink(defaultConfig);
19
+ const apiPath = link.versionedApiPath;
6
20
  const actionPrefix = "dhis-web-commons/";
7
21
  const actionPostfix = '.action';
8
22
  describe('queryToResourcePath', () => {
@@ -11,7 +25,7 @@ describe('queryToResourcePath', () => {
11
25
  const query = {
12
26
  resource: 'action::test'
13
27
  };
14
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(actionPrefix, "test").concat(actionPostfix));
28
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(actionPrefix, "test").concat(actionPostfix));
15
29
  });
16
30
  it('should return action URL with a simple querystring if query parameters are passed', () => {
17
31
  const query = {
@@ -20,7 +34,7 @@ describe('queryToResourcePath', () => {
20
34
  key: 'value'
21
35
  }
22
36
  };
23
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(actionPrefix, "test").concat(actionPostfix, "?key=value"));
37
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(actionPrefix, "test").concat(actionPostfix, "?key=value"));
24
38
  });
25
39
  });
26
40
  describe('resource with dot', () => {
@@ -28,14 +42,14 @@ describe('queryToResourcePath', () => {
28
42
  const query = {
29
43
  resource: 'svg.pdf'
30
44
  };
31
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(apiPath, "/svg.pdf"));
45
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/svg.pdf"));
32
46
  });
33
47
  });
34
48
  it('should return resource url with no querystring if not query parameters are passed', () => {
35
49
  const query = {
36
50
  resource: 'test'
37
51
  };
38
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(apiPath, "/test"));
52
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test"));
39
53
  });
40
54
  it('should return resource url and singular parameter separated by ?', () => {
41
55
  const query = {
@@ -44,7 +58,7 @@ describe('queryToResourcePath', () => {
44
58
  key: 'value'
45
59
  }
46
60
  };
47
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(apiPath, "/test?key=value"));
61
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value"));
48
62
  });
49
63
  it('should return resource url and multiple parameters separated by ? and &', () => {
50
64
  const query = {
@@ -54,7 +68,7 @@ describe('queryToResourcePath', () => {
54
68
  param: 'value2'
55
69
  }
56
70
  };
57
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(apiPath, "/test?key=value&param=value2"));
71
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value&param=value2"));
58
72
  });
59
73
  it('should url encode special characters in query keys', () => {
60
74
  const query = {
@@ -63,7 +77,7 @@ describe('queryToResourcePath', () => {
63
77
  'key=42&val': 'value'
64
78
  }
65
79
  };
66
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(apiPath, "/test?key%3D42%26val=value"));
80
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key%3D42%26val=value"));
67
81
  });
68
82
  it('should url encode special characters in string parameters', () => {
69
83
  const query = {
@@ -73,7 +87,7 @@ describe('queryToResourcePath', () => {
73
87
  param: 'value2&& 53'
74
88
  }
75
89
  };
76
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(apiPath, "/test?key=value%3F%3D42&param=value2%26%26%2053"));
90
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value%3F%3D42&param=value2%26%26%2053"));
77
91
  });
78
92
  it('should support numeric (integer and float) parameters', () => {
79
93
  const query = {
@@ -83,7 +97,7 @@ describe('queryToResourcePath', () => {
83
97
  param: 193.75
84
98
  }
85
99
  };
86
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(apiPath, "/test?key=42&param=193.75"));
100
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=42&param=193.75"));
87
101
  });
88
102
  it('should support boolean parameters', () => {
89
103
  const query = {
@@ -93,7 +107,7 @@ describe('queryToResourcePath', () => {
93
107
  someflag: true
94
108
  }
95
109
  };
96
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(apiPath, "/test?key=42&someflag=true"));
110
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=42&someflag=true"));
97
111
  });
98
112
  it('should join array parameters with commas', () => {
99
113
  const query = {
@@ -102,7 +116,7 @@ describe('queryToResourcePath', () => {
102
116
  key: ['asdf', 123]
103
117
  }
104
118
  };
105
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(apiPath, "/test?key=asdf,123"));
119
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=asdf,123"));
106
120
  });
107
121
  it('should include multiple filter parameters when array of filters provided', () => {
108
122
  const query = {
@@ -111,7 +125,7 @@ describe('queryToResourcePath', () => {
111
125
  filter: ['asdf', 123]
112
126
  }
113
127
  };
114
- expect((0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toBe("".concat(apiPath, "/test?filter=asdf&filter=123"));
128
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?filter=asdf&filter=123"));
115
129
  });
116
130
  it('should NOT YET support name-aliased parameters', () => {
117
131
  const query = {
@@ -122,7 +136,7 @@ describe('queryToResourcePath', () => {
122
136
  }
123
137
  }
124
138
  };
125
- expect(() => (0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toThrow();
139
+ expect(() => (0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toThrow();
126
140
  });
127
141
  it('should throw if passed something crazy like a function', () => {
128
142
  const query = {
@@ -131,6 +145,31 @@ describe('queryToResourcePath', () => {
131
145
  key: a => a
132
146
  }
133
147
  };
134
- expect(() => (0, _queryToResourcePath.queryToResourcePath)(apiPath, query)).toThrow();
148
+ expect(() => (0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toThrow();
149
+ });
150
+ it('should return an unversioned endpoint for the new tracker importer (in version 2.37)', () => {
151
+ const query = {
152
+ resource: 'tracker'
153
+ };
154
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(link.unversionedApiPath, "/tracker"));
155
+ });
156
+ it('should return an unversioned endpoint sub-resources of the new tracker importer (in version 2.37)', () => {
157
+ const query = {
158
+ resource: 'tracker/test'
159
+ };
160
+ expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(link.unversionedApiPath, "/tracker/test"));
161
+ });
162
+ it('should return a VERSIONED endpoint for the new tracker importer (in version 2.38)', () => {
163
+ const query = {
164
+ resource: 'tracker'
165
+ };
166
+ const v38config = { ...defaultConfig,
167
+ serverVersion: {
168
+ major: 2,
169
+ minor: 38,
170
+ patch: 0
171
+ }
172
+ };
173
+ expect((0, _queryToResourcePath.queryToResourcePath)(createLink(v38config), query, 'read')).toBe("".concat(link.versionedApiPath, "/tracker"));
135
174
  });
136
175
  });
@@ -16,29 +16,26 @@ var _queryToResourcePath = require("./RestAPILink/queryToResourcePath");
16
16
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
17
17
 
18
18
  class RestAPILink {
19
- constructor({
20
- baseUrl,
21
- apiVersion
22
- }) {
23
- _defineProperty(this, "apiPath", void 0);
19
+ constructor(config) {
20
+ _defineProperty(this, "config", void 0);
24
21
 
25
- _defineProperty(this, "baseUrl", void 0);
22
+ _defineProperty(this, "versionedApiPath", void 0);
26
23
 
27
- _defineProperty(this, "apiVersion", void 0);
24
+ _defineProperty(this, "unversionedApiPath", void 0);
28
25
 
29
- this.baseUrl = baseUrl;
30
- this.apiVersion = apiVersion;
31
- this.apiPath = (0, _path.joinPath)('api', String(apiVersion));
26
+ this.config = config;
27
+ this.versionedApiPath = (0, _path.joinPath)('api', String(config.apiVersion));
28
+ this.unversionedApiPath = (0, _path.joinPath)('api');
32
29
  }
33
30
 
34
31
  fetch(path, options) {
35
- return (0, _fetchData.fetchData)((0, _path.joinPath)(this.baseUrl, path), options);
32
+ return (0, _fetchData.fetchData)((0, _path.joinPath)(this.config.baseUrl, path), options);
36
33
  }
37
34
 
38
35
  executeResourceQuery(type, query, {
39
36
  signal
40
37
  }) {
41
- return this.fetch((0, _queryToResourcePath.queryToResourcePath)(this.apiPath, query, type), (0, _queryToRequestOptions.queryToRequestOptions)(type, query, signal));
38
+ return this.fetch((0, _queryToResourcePath.queryToResourcePath)(this, query, type), (0, _queryToRequestOptions.queryToRequestOptions)(type, query, signal));
42
39
  }
43
40
 
44
41
  }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.mergeAndCompareVariables = void 0;
7
+
8
+ var _stableVariablesHash = require("./stableVariablesHash");
9
+
10
+ const mergeAndCompareVariables = (previousVariables, newVariables, previousHash) => {
11
+ if (!newVariables) {
12
+ return {
13
+ identical: true,
14
+ mergedVariablesHash: previousHash,
15
+ mergedVariables: previousVariables
16
+ };
17
+ } // Use cached hash if it exists
18
+
19
+
20
+ const currentHash = previousHash || (0, _stableVariablesHash.stableVariablesHash)(previousVariables);
21
+ const mergedVariables = { ...previousVariables,
22
+ ...newVariables
23
+ };
24
+ const mergedVariablesHash = (0, _stableVariablesHash.stableVariablesHash)(mergedVariables);
25
+ const identical = currentHash === mergedVariablesHash;
26
+ return {
27
+ identical,
28
+ mergedVariablesHash,
29
+ mergedVariables
30
+ };
31
+ };
32
+
33
+ exports.mergeAndCompareVariables = mergeAndCompareVariables;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+
3
+ var _mergeAndCompareVariables = require("./mergeAndCompareVariables");
4
+
5
+ var _stableVariablesHash = require("./stableVariablesHash");
6
+
7
+ jest.mock('./stableVariablesHash', () => ({
8
+ stableVariablesHash: object => JSON.stringify(object)
9
+ }));
10
+ const testVariables = {
11
+ question: 'What do you get when you multiply six by nine?',
12
+ answer: 42
13
+ };
14
+ const testHash = (0, _stableVariablesHash.stableVariablesHash)(testVariables);
15
+ describe('mergeAndCompareVariables', () => {
16
+ it('Should return previous variables and hash when no new variables are provided', () => {
17
+ expect((0, _mergeAndCompareVariables.mergeAndCompareVariables)(testVariables, undefined, undefined)).toMatchObject({
18
+ identical: true,
19
+ mergedVariables: testVariables,
20
+ mergedVariablesHash: undefined
21
+ });
22
+ });
23
+ it('Should return identical: true when merged variables are identical to old variables (without prev hash)', () => {
24
+ const newVariables = {
25
+ answer: testVariables.answer
26
+ };
27
+ expect((0, _mergeAndCompareVariables.mergeAndCompareVariables)(testVariables, newVariables, undefined)).toMatchObject({
28
+ identical: true,
29
+ mergedVariables: testVariables,
30
+ mergedVariablesHash: testHash
31
+ });
32
+ });
33
+ it('Should return identical: false with incorrect previous hash', () => {
34
+ const incorrectPreviousHash = 'IAmAHash';
35
+ const newVariables = {
36
+ answer: 42
37
+ };
38
+ expect((0, _mergeAndCompareVariables.mergeAndCompareVariables)(testVariables, newVariables, incorrectPreviousHash)).toMatchObject({
39
+ identical: false,
40
+ mergedVariables: testVariables,
41
+ mergedVariablesHash: testHash
42
+ });
43
+ });
44
+ it('Should return identical: false when merged variables are different than old variables', () => {
45
+ const newVariables = {
46
+ answer: 43
47
+ };
48
+ const expectedMergedVariables = { ...testVariables,
49
+ ...newVariables
50
+ };
51
+ expect((0, _mergeAndCompareVariables.mergeAndCompareVariables)(testVariables, newVariables, testHash)).toMatchObject({
52
+ identical: false,
53
+ mergedVariables: expectedMergedVariables,
54
+ mergedVariablesHash: (0, _stableVariablesHash.stableVariablesHash)(expectedMergedVariables)
55
+ });
56
+ });
57
+ });
@@ -9,7 +9,7 @@ var _react = require("react");
9
9
 
10
10
  var _reactQuery = require("react-query");
11
11
 
12
- var _stableVariablesHash = require("./stableVariablesHash");
12
+ var _mergeAndCompareVariables = require("./mergeAndCompareVariables");
13
13
 
14
14
  var _useDataEngine = require("./useDataEngine");
15
15
 
@@ -35,24 +35,35 @@ const useDataQuery = (query, {
35
35
  variables: initialVariables = {},
36
36
  lazy: initialLazy = false
37
37
  } = {}) => {
38
- const variablesHash = (0, _react.useRef)(null);
39
- const [variables, setVariables] = (0, _react.useState)(initialVariables);
40
- const [enabled, setEnabled] = (0, _react.useState)(!initialLazy);
41
38
  const [staticQuery] = (0, _useStaticInput.useStaticInput)(query, {
42
39
  warn: true,
43
40
  name: 'query'
44
41
  });
42
+ const [variablesUpdateCount, setVariablesUpdateCount] = (0, _react.useState)(0);
43
+ const queryState = (0, _react.useRef)({
44
+ variables: initialVariables,
45
+ variablesHash: undefined,
46
+ enabled: !initialLazy,
47
+ refetchCallback: undefined
48
+ });
45
49
  /**
46
- * User callbacks and refetch handling
50
+ * Display current query state and refetch count in React DevTools
47
51
  */
48
52
 
49
- const refetchCallback = (0, _react.useRef)(null);
53
+ (0, _react.useDebugValue)({
54
+ variablesUpdateCount,
55
+ enabled: queryState.current.enabled,
56
+ variables: queryState.current.variables
57
+ }, debugValue => JSON.stringify(debugValue));
58
+ /**
59
+ * User callbacks and refetch handling
60
+ */
50
61
 
51
62
  const onSuccess = data => {
52
- if (refetchCallback.current) {
53
- refetchCallback.current(data);
54
- refetchCallback.current = null;
55
- }
63
+ var _queryState$current$r, _queryState$current;
64
+
65
+ (_queryState$current$r = (_queryState$current = queryState.current).refetchCallback) === null || _queryState$current$r === void 0 ? void 0 : _queryState$current$r.call(_queryState$current, data);
66
+ queryState.current.refetchCallback = undefined;
56
67
 
57
68
  if (userOnSuccess) {
58
69
  userOnSuccess(data);
@@ -61,9 +72,7 @@ const useDataQuery = (query, {
61
72
 
62
73
  const onError = error => {
63
74
  // If we'd want to reject on errors we'd call the cb with the error here
64
- if (refetchCallback.current) {
65
- refetchCallback.current = null;
66
- }
75
+ queryState.current.refetchCallback = undefined;
67
76
 
68
77
  if (userOnError) {
69
78
  userOnError(error);
@@ -75,10 +84,10 @@ const useDataQuery = (query, {
75
84
 
76
85
 
77
86
  const engine = (0, _useDataEngine.useDataEngine)();
78
- const queryKey = [staticQuery, variables];
87
+ const queryKey = [staticQuery, queryState.current.variables];
79
88
 
80
89
  const queryFn = () => engine.query(staticQuery, {
81
- variables
90
+ variables: queryState.current.variables
82
91
  });
83
92
 
84
93
  const {
@@ -89,7 +98,7 @@ const useDataQuery = (query, {
89
98
  data,
90
99
  refetch: queryRefetch
91
100
  } = (0, _reactQuery.useQuery)(queryKey, queryFn, {
92
- enabled,
101
+ enabled: queryState.current.enabled,
93
102
  onSuccess,
94
103
  onError
95
104
  });
@@ -103,11 +112,17 @@ const useDataQuery = (query, {
103
112
  */
104
113
 
105
114
  const refetch = (0, _react.useCallback)(newVariables => {
115
+ const {
116
+ identical,
117
+ mergedVariables,
118
+ mergedVariablesHash
119
+ } = (0, _mergeAndCompareVariables.mergeAndCompareVariables)(queryState.current.variables, newVariables, queryState.current.variablesHash);
106
120
  /**
107
121
  * If there are no updates that will trigger an automatic refetch
108
122
  * we'll need to call react-query's refetch directly
109
123
  */
110
- if (enabled && !newVariables) {
124
+
125
+ if (queryState.current.enabled && identical) {
111
126
  return queryRefetch({
112
127
  cancelRefetch: true,
113
128
  throwOnError: false
@@ -116,44 +131,19 @@ const useDataQuery = (query, {
116
131
  }) => data);
117
132
  }
118
133
 
119
- if (newVariables) {
120
- // Use cached hash if it exists
121
- const currentHash = variablesHash.current || (0, _stableVariablesHash.stableVariablesHash)(variables);
122
- const mergedVariables = { ...variables,
123
- ...newVariables
124
- };
125
- const mergedHash = (0, _stableVariablesHash.stableVariablesHash)(mergedVariables);
126
- const identical = currentHash === mergedHash;
127
-
128
- if (identical && enabled) {
129
- /**
130
- * If the variables are identical and the query is enabled
131
- * we'll need to trigger the refetch manually
132
- */
133
- return queryRefetch({
134
- cancelRefetch: true,
135
- throwOnError: false
136
- }).then(({
137
- data
138
- }) => data);
139
- } else {
140
- variablesHash.current = mergedHash;
141
- setVariables(mergedVariables);
142
- }
143
- } // Enable the query after the variables have been set to prevent extra request
144
-
145
-
146
- if (!enabled) {
147
- setEnabled(true);
148
- } // This promise does not currently reject on errors
149
-
150
-
151
- return new Promise(resolve => {
152
- refetchCallback.current = data => {
134
+ queryState.current.variables = mergedVariables;
135
+ queryState.current.variablesHash = mergedVariablesHash;
136
+ queryState.current.enabled = true; // This promise does not currently reject on errors
137
+
138
+ const refetchPromise = new Promise(resolve => {
139
+ queryState.current.refetchCallback = data => {
153
140
  resolve(data);
154
141
  };
155
- });
156
- }, [enabled, queryRefetch, variables]);
142
+ }); // Trigger a react-query refetch by incrementing variablesUpdateCount state
143
+
144
+ setVariablesUpdateCount(prevCount => prevCount + 1);
145
+ return refetchPromise;
146
+ }, [queryRefetch]);
157
147
  /**
158
148
  * react-query returns null or an error, but we return undefined
159
149
  * or an error, so this ensures consistency with the other types.
@@ -507,6 +507,65 @@ describe('useDataQuery', () => {
507
507
  });
508
508
  });
509
509
  describe('return values: refetch', () => {
510
+ it('Should be stable if the query variables change', async () => {
511
+ let count = 0;
512
+ const spy = jest.fn(() => {
513
+ count++;
514
+ return count;
515
+ });
516
+ const data = {
517
+ answer: spy
518
+ };
519
+ const query = {
520
+ x: {
521
+ resource: 'answer'
522
+ }
523
+ };
524
+
525
+ const wrapper = ({
526
+ children
527
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
528
+ data: data
529
+ }, children);
530
+
531
+ const {
532
+ result,
533
+ waitFor
534
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataQuery.useDataQuery)(query, {
535
+ lazy: true
536
+ }), {
537
+ wrapper
538
+ });
539
+ expect(spy).not.toHaveBeenCalled();
540
+ const initialRefetch = result.current.refetch;
541
+ (0, _reactHooks.act)(() => {
542
+ initialRefetch();
543
+ });
544
+ await waitFor(() => {
545
+ expect(result.current).toMatchObject({
546
+ loading: false,
547
+ called: true,
548
+ data: {
549
+ x: 1
550
+ }
551
+ });
552
+ });
553
+ expect(spy).toHaveBeenCalledTimes(1);
554
+ (0, _reactHooks.act)(() => {
555
+ initialRefetch();
556
+ });
557
+ await waitFor(() => {
558
+ expect(result.current).toMatchObject({
559
+ loading: false,
560
+ called: true,
561
+ data: {
562
+ x: 2
563
+ }
564
+ });
565
+ });
566
+ expect(spy).toHaveBeenCalledTimes(2);
567
+ expect(initialRefetch).toBe(result.current.refetch);
568
+ });
510
569
  it('Should only trigger a single request when refetch is called on a lazy query with new variables', async () => {
511
570
  const spy = jest.fn((type, query) => {
512
571
  if (query.id === '1') {
@@ -13,6 +13,7 @@ const useStaticInput = (staticValue, {
13
13
  } = {}) => {
14
14
  const originalValue = (0, _react.useRef)(staticValue);
15
15
  const [value, setValue] = (0, _react.useState)(() => originalValue.current);
16
+ (0, _react.useDebugValue)(value, debugValue => "".concat(name, ": ").concat(JSON.stringify(debugValue)));
16
17
  (0, _react.useEffect)(() => {
17
18
  if (warn && originalValue.current !== staticValue) {
18
19
  console.warn("The ".concat(name, " should be static, don't create it within the render loop!"));
@@ -55,13 +55,26 @@ const isAction = resource => resource.startsWith(actionPrefix);
55
55
 
56
56
  const makeActionPath = resource => joinPath('dhis-web-commons', "".concat(resource.substr(actionPrefix.length), ".action"));
57
57
 
58
- export const queryToResourcePath = (apiPath, query, type) => {
58
+ const skipApiVersion = (resource, config) => {
59
+ if (resource === 'tracker' || resource.startsWith('tracker/')) {
60
+ var _config$serverVersion, _config$serverVersion2;
61
+
62
+ if (!((_config$serverVersion = config.serverVersion) !== null && _config$serverVersion !== void 0 && _config$serverVersion.minor) || ((_config$serverVersion2 = config.serverVersion) === null || _config$serverVersion2 === void 0 ? void 0 : _config$serverVersion2.minor) < 38) {
63
+ return true;
64
+ }
65
+ }
66
+
67
+ return false;
68
+ };
69
+
70
+ export const queryToResourcePath = (link, query, type) => {
59
71
  const {
60
72
  resource,
61
73
  id,
62
74
  params = {}
63
75
  } = query;
64
- const base = isAction(resource) ? makeActionPath(resource) : joinPath(apiPath, resource, id);
76
+ const apiBase = skipApiVersion(resource, link.config) ? link.unversionedApiPath : link.versionedApiPath;
77
+ const base = isAction(resource) ? makeActionPath(resource) : joinPath(apiBase, resource, id);
65
78
  validateResourceQuery(query, type);
66
79
 
67
80
  if (Object.keys(params).length) {
@@ -1,5 +1,19 @@
1
+ import { RestAPILink } from '../RestAPILink';
1
2
  import { queryToResourcePath } from './queryToResourcePath';
2
- const apiPath = '<api>';
3
+
4
+ const createLink = config => new RestAPILink(config);
5
+
6
+ const defaultConfig = {
7
+ basePath: '<base>',
8
+ apiVersion: '37',
9
+ serverVersion: {
10
+ major: 2,
11
+ minor: 37,
12
+ patch: 11
13
+ }
14
+ };
15
+ const link = createLink(defaultConfig);
16
+ const apiPath = link.versionedApiPath;
3
17
  const actionPrefix = "dhis-web-commons/";
4
18
  const actionPostfix = '.action';
5
19
  describe('queryToResourcePath', () => {
@@ -8,7 +22,7 @@ describe('queryToResourcePath', () => {
8
22
  const query = {
9
23
  resource: 'action::test'
10
24
  };
11
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(actionPrefix, "test").concat(actionPostfix));
25
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(actionPrefix, "test").concat(actionPostfix));
12
26
  });
13
27
  it('should return action URL with a simple querystring if query parameters are passed', () => {
14
28
  const query = {
@@ -17,7 +31,7 @@ describe('queryToResourcePath', () => {
17
31
  key: 'value'
18
32
  }
19
33
  };
20
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(actionPrefix, "test").concat(actionPostfix, "?key=value"));
34
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(actionPrefix, "test").concat(actionPostfix, "?key=value"));
21
35
  });
22
36
  });
23
37
  describe('resource with dot', () => {
@@ -25,14 +39,14 @@ describe('queryToResourcePath', () => {
25
39
  const query = {
26
40
  resource: 'svg.pdf'
27
41
  };
28
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(apiPath, "/svg.pdf"));
42
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/svg.pdf"));
29
43
  });
30
44
  });
31
45
  it('should return resource url with no querystring if not query parameters are passed', () => {
32
46
  const query = {
33
47
  resource: 'test'
34
48
  };
35
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(apiPath, "/test"));
49
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test"));
36
50
  });
37
51
  it('should return resource url and singular parameter separated by ?', () => {
38
52
  const query = {
@@ -41,7 +55,7 @@ describe('queryToResourcePath', () => {
41
55
  key: 'value'
42
56
  }
43
57
  };
44
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(apiPath, "/test?key=value"));
58
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value"));
45
59
  });
46
60
  it('should return resource url and multiple parameters separated by ? and &', () => {
47
61
  const query = {
@@ -51,7 +65,7 @@ describe('queryToResourcePath', () => {
51
65
  param: 'value2'
52
66
  }
53
67
  };
54
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(apiPath, "/test?key=value&param=value2"));
68
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value&param=value2"));
55
69
  });
56
70
  it('should url encode special characters in query keys', () => {
57
71
  const query = {
@@ -60,7 +74,7 @@ describe('queryToResourcePath', () => {
60
74
  'key=42&val': 'value'
61
75
  }
62
76
  };
63
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(apiPath, "/test?key%3D42%26val=value"));
77
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key%3D42%26val=value"));
64
78
  });
65
79
  it('should url encode special characters in string parameters', () => {
66
80
  const query = {
@@ -70,7 +84,7 @@ describe('queryToResourcePath', () => {
70
84
  param: 'value2&& 53'
71
85
  }
72
86
  };
73
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(apiPath, "/test?key=value%3F%3D42&param=value2%26%26%2053"));
87
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value%3F%3D42&param=value2%26%26%2053"));
74
88
  });
75
89
  it('should support numeric (integer and float) parameters', () => {
76
90
  const query = {
@@ -80,7 +94,7 @@ describe('queryToResourcePath', () => {
80
94
  param: 193.75
81
95
  }
82
96
  };
83
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(apiPath, "/test?key=42&param=193.75"));
97
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=42&param=193.75"));
84
98
  });
85
99
  it('should support boolean parameters', () => {
86
100
  const query = {
@@ -90,7 +104,7 @@ describe('queryToResourcePath', () => {
90
104
  someflag: true
91
105
  }
92
106
  };
93
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(apiPath, "/test?key=42&someflag=true"));
107
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=42&someflag=true"));
94
108
  });
95
109
  it('should join array parameters with commas', () => {
96
110
  const query = {
@@ -99,7 +113,7 @@ describe('queryToResourcePath', () => {
99
113
  key: ['asdf', 123]
100
114
  }
101
115
  };
102
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(apiPath, "/test?key=asdf,123"));
116
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=asdf,123"));
103
117
  });
104
118
  it('should include multiple filter parameters when array of filters provided', () => {
105
119
  const query = {
@@ -108,7 +122,7 @@ describe('queryToResourcePath', () => {
108
122
  filter: ['asdf', 123]
109
123
  }
110
124
  };
111
- expect(queryToResourcePath(apiPath, query)).toBe("".concat(apiPath, "/test?filter=asdf&filter=123"));
125
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?filter=asdf&filter=123"));
112
126
  });
113
127
  it('should NOT YET support name-aliased parameters', () => {
114
128
  const query = {
@@ -119,7 +133,7 @@ describe('queryToResourcePath', () => {
119
133
  }
120
134
  }
121
135
  };
122
- expect(() => queryToResourcePath(apiPath, query)).toThrow();
136
+ expect(() => queryToResourcePath(link, query, 'read')).toThrow();
123
137
  });
124
138
  it('should throw if passed something crazy like a function', () => {
125
139
  const query = {
@@ -128,6 +142,31 @@ describe('queryToResourcePath', () => {
128
142
  key: a => a
129
143
  }
130
144
  };
131
- expect(() => queryToResourcePath(apiPath, query)).toThrow();
145
+ expect(() => queryToResourcePath(link, query, 'read')).toThrow();
146
+ });
147
+ it('should return an unversioned endpoint for the new tracker importer (in version 2.37)', () => {
148
+ const query = {
149
+ resource: 'tracker'
150
+ };
151
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(link.unversionedApiPath, "/tracker"));
152
+ });
153
+ it('should return an unversioned endpoint sub-resources of the new tracker importer (in version 2.37)', () => {
154
+ const query = {
155
+ resource: 'tracker/test'
156
+ };
157
+ expect(queryToResourcePath(link, query, 'read')).toBe("".concat(link.unversionedApiPath, "/tracker/test"));
158
+ });
159
+ it('should return a VERSIONED endpoint for the new tracker importer (in version 2.38)', () => {
160
+ const query = {
161
+ resource: 'tracker'
162
+ };
163
+ const v38config = { ...defaultConfig,
164
+ serverVersion: {
165
+ major: 2,
166
+ minor: 38,
167
+ patch: 0
168
+ }
169
+ };
170
+ expect(queryToResourcePath(createLink(v38config), query, 'read')).toBe("".concat(link.versionedApiPath, "/tracker"));
132
171
  });
133
172
  });
@@ -5,29 +5,26 @@ import { joinPath } from './RestAPILink/path';
5
5
  import { queryToRequestOptions } from './RestAPILink/queryToRequestOptions';
6
6
  import { queryToResourcePath } from './RestAPILink/queryToResourcePath';
7
7
  export class RestAPILink {
8
- constructor({
9
- baseUrl,
10
- apiVersion
11
- }) {
12
- _defineProperty(this, "apiPath", void 0);
8
+ constructor(config) {
9
+ _defineProperty(this, "config", void 0);
13
10
 
14
- _defineProperty(this, "baseUrl", void 0);
11
+ _defineProperty(this, "versionedApiPath", void 0);
15
12
 
16
- _defineProperty(this, "apiVersion", void 0);
13
+ _defineProperty(this, "unversionedApiPath", void 0);
17
14
 
18
- this.baseUrl = baseUrl;
19
- this.apiVersion = apiVersion;
20
- this.apiPath = joinPath('api', String(apiVersion));
15
+ this.config = config;
16
+ this.versionedApiPath = joinPath('api', String(config.apiVersion));
17
+ this.unversionedApiPath = joinPath('api');
21
18
  }
22
19
 
23
20
  fetch(path, options) {
24
- return fetchData(joinPath(this.baseUrl, path), options);
21
+ return fetchData(joinPath(this.config.baseUrl, path), options);
25
22
  }
26
23
 
27
24
  executeResourceQuery(type, query, {
28
25
  signal
29
26
  }) {
30
- return this.fetch(queryToResourcePath(this.apiPath, query, type), queryToRequestOptions(type, query, signal));
27
+ return this.fetch(queryToResourcePath(this, query, type), queryToRequestOptions(type, query, signal));
31
28
  }
32
29
 
33
30
  }
@@ -0,0 +1,23 @@
1
+ import { stableVariablesHash } from './stableVariablesHash';
2
+ export const mergeAndCompareVariables = (previousVariables, newVariables, previousHash) => {
3
+ if (!newVariables) {
4
+ return {
5
+ identical: true,
6
+ mergedVariablesHash: previousHash,
7
+ mergedVariables: previousVariables
8
+ };
9
+ } // Use cached hash if it exists
10
+
11
+
12
+ const currentHash = previousHash || stableVariablesHash(previousVariables);
13
+ const mergedVariables = { ...previousVariables,
14
+ ...newVariables
15
+ };
16
+ const mergedVariablesHash = stableVariablesHash(mergedVariables);
17
+ const identical = currentHash === mergedVariablesHash;
18
+ return {
19
+ identical,
20
+ mergedVariablesHash,
21
+ mergedVariables
22
+ };
23
+ };
@@ -0,0 +1,53 @@
1
+ import { mergeAndCompareVariables } from './mergeAndCompareVariables';
2
+ import { stableVariablesHash } from './stableVariablesHash';
3
+ jest.mock('./stableVariablesHash', () => ({
4
+ stableVariablesHash: object => JSON.stringify(object)
5
+ }));
6
+ const testVariables = {
7
+ question: 'What do you get when you multiply six by nine?',
8
+ answer: 42
9
+ };
10
+ const testHash = stableVariablesHash(testVariables);
11
+ describe('mergeAndCompareVariables', () => {
12
+ it('Should return previous variables and hash when no new variables are provided', () => {
13
+ expect(mergeAndCompareVariables(testVariables, undefined, undefined)).toMatchObject({
14
+ identical: true,
15
+ mergedVariables: testVariables,
16
+ mergedVariablesHash: undefined
17
+ });
18
+ });
19
+ it('Should return identical: true when merged variables are identical to old variables (without prev hash)', () => {
20
+ const newVariables = {
21
+ answer: testVariables.answer
22
+ };
23
+ expect(mergeAndCompareVariables(testVariables, newVariables, undefined)).toMatchObject({
24
+ identical: true,
25
+ mergedVariables: testVariables,
26
+ mergedVariablesHash: testHash
27
+ });
28
+ });
29
+ it('Should return identical: false with incorrect previous hash', () => {
30
+ const incorrectPreviousHash = 'IAmAHash';
31
+ const newVariables = {
32
+ answer: 42
33
+ };
34
+ expect(mergeAndCompareVariables(testVariables, newVariables, incorrectPreviousHash)).toMatchObject({
35
+ identical: false,
36
+ mergedVariables: testVariables,
37
+ mergedVariablesHash: testHash
38
+ });
39
+ });
40
+ it('Should return identical: false when merged variables are different than old variables', () => {
41
+ const newVariables = {
42
+ answer: 43
43
+ };
44
+ const expectedMergedVariables = { ...testVariables,
45
+ ...newVariables
46
+ };
47
+ expect(mergeAndCompareVariables(testVariables, newVariables, testHash)).toMatchObject({
48
+ identical: false,
49
+ mergedVariables: expectedMergedVariables,
50
+ mergedVariablesHash: stableVariablesHash(expectedMergedVariables)
51
+ });
52
+ });
53
+ });
@@ -1,6 +1,6 @@
1
- import { useState, useRef, useCallback } from 'react';
1
+ import { useState, useRef, useCallback, useDebugValue } from 'react';
2
2
  import { useQuery, setLogger } from 'react-query';
3
- import { stableVariablesHash } from './stableVariablesHash';
3
+ import { mergeAndCompareVariables } from './mergeAndCompareVariables';
4
4
  import { useDataEngine } from './useDataEngine';
5
5
  import { useStaticInput } from './useStaticInput';
6
6
 
@@ -23,24 +23,35 @@ export const useDataQuery = (query, {
23
23
  variables: initialVariables = {},
24
24
  lazy: initialLazy = false
25
25
  } = {}) => {
26
- const variablesHash = useRef(null);
27
- const [variables, setVariables] = useState(initialVariables);
28
- const [enabled, setEnabled] = useState(!initialLazy);
29
26
  const [staticQuery] = useStaticInput(query, {
30
27
  warn: true,
31
28
  name: 'query'
32
29
  });
30
+ const [variablesUpdateCount, setVariablesUpdateCount] = useState(0);
31
+ const queryState = useRef({
32
+ variables: initialVariables,
33
+ variablesHash: undefined,
34
+ enabled: !initialLazy,
35
+ refetchCallback: undefined
36
+ });
33
37
  /**
34
- * User callbacks and refetch handling
38
+ * Display current query state and refetch count in React DevTools
35
39
  */
36
40
 
37
- const refetchCallback = useRef(null);
41
+ useDebugValue({
42
+ variablesUpdateCount,
43
+ enabled: queryState.current.enabled,
44
+ variables: queryState.current.variables
45
+ }, debugValue => JSON.stringify(debugValue));
46
+ /**
47
+ * User callbacks and refetch handling
48
+ */
38
49
 
39
50
  const onSuccess = data => {
40
- if (refetchCallback.current) {
41
- refetchCallback.current(data);
42
- refetchCallback.current = null;
43
- }
51
+ var _queryState$current$r, _queryState$current;
52
+
53
+ (_queryState$current$r = (_queryState$current = queryState.current).refetchCallback) === null || _queryState$current$r === void 0 ? void 0 : _queryState$current$r.call(_queryState$current, data);
54
+ queryState.current.refetchCallback = undefined;
44
55
 
45
56
  if (userOnSuccess) {
46
57
  userOnSuccess(data);
@@ -49,9 +60,7 @@ export const useDataQuery = (query, {
49
60
 
50
61
  const onError = error => {
51
62
  // If we'd want to reject on errors we'd call the cb with the error here
52
- if (refetchCallback.current) {
53
- refetchCallback.current = null;
54
- }
63
+ queryState.current.refetchCallback = undefined;
55
64
 
56
65
  if (userOnError) {
57
66
  userOnError(error);
@@ -63,10 +72,10 @@ export const useDataQuery = (query, {
63
72
 
64
73
 
65
74
  const engine = useDataEngine();
66
- const queryKey = [staticQuery, variables];
75
+ const queryKey = [staticQuery, queryState.current.variables];
67
76
 
68
77
  const queryFn = () => engine.query(staticQuery, {
69
- variables
78
+ variables: queryState.current.variables
70
79
  });
71
80
 
72
81
  const {
@@ -77,7 +86,7 @@ export const useDataQuery = (query, {
77
86
  data,
78
87
  refetch: queryRefetch
79
88
  } = useQuery(queryKey, queryFn, {
80
- enabled,
89
+ enabled: queryState.current.enabled,
81
90
  onSuccess,
82
91
  onError
83
92
  });
@@ -91,11 +100,17 @@ export const useDataQuery = (query, {
91
100
  */
92
101
 
93
102
  const refetch = useCallback(newVariables => {
103
+ const {
104
+ identical,
105
+ mergedVariables,
106
+ mergedVariablesHash
107
+ } = mergeAndCompareVariables(queryState.current.variables, newVariables, queryState.current.variablesHash);
94
108
  /**
95
109
  * If there are no updates that will trigger an automatic refetch
96
110
  * we'll need to call react-query's refetch directly
97
111
  */
98
- if (enabled && !newVariables) {
112
+
113
+ if (queryState.current.enabled && identical) {
99
114
  return queryRefetch({
100
115
  cancelRefetch: true,
101
116
  throwOnError: false
@@ -104,44 +119,19 @@ export const useDataQuery = (query, {
104
119
  }) => data);
105
120
  }
106
121
 
107
- if (newVariables) {
108
- // Use cached hash if it exists
109
- const currentHash = variablesHash.current || stableVariablesHash(variables);
110
- const mergedVariables = { ...variables,
111
- ...newVariables
112
- };
113
- const mergedHash = stableVariablesHash(mergedVariables);
114
- const identical = currentHash === mergedHash;
115
-
116
- if (identical && enabled) {
117
- /**
118
- * If the variables are identical and the query is enabled
119
- * we'll need to trigger the refetch manually
120
- */
121
- return queryRefetch({
122
- cancelRefetch: true,
123
- throwOnError: false
124
- }).then(({
125
- data
126
- }) => data);
127
- } else {
128
- variablesHash.current = mergedHash;
129
- setVariables(mergedVariables);
130
- }
131
- } // Enable the query after the variables have been set to prevent extra request
132
-
133
-
134
- if (!enabled) {
135
- setEnabled(true);
136
- } // This promise does not currently reject on errors
137
-
138
-
139
- return new Promise(resolve => {
140
- refetchCallback.current = data => {
122
+ queryState.current.variables = mergedVariables;
123
+ queryState.current.variablesHash = mergedVariablesHash;
124
+ queryState.current.enabled = true; // This promise does not currently reject on errors
125
+
126
+ const refetchPromise = new Promise(resolve => {
127
+ queryState.current.refetchCallback = data => {
141
128
  resolve(data);
142
129
  };
143
- });
144
- }, [enabled, queryRefetch, variables]);
130
+ }); // Trigger a react-query refetch by incrementing variablesUpdateCount state
131
+
132
+ setVariablesUpdateCount(prevCount => prevCount + 1);
133
+ return refetchPromise;
134
+ }, [queryRefetch]);
145
135
  /**
146
136
  * react-query returns null or an error, but we return undefined
147
137
  * or an error, so this ensures consistency with the other types.
@@ -497,6 +497,65 @@ describe('useDataQuery', () => {
497
497
  });
498
498
  });
499
499
  describe('return values: refetch', () => {
500
+ it('Should be stable if the query variables change', async () => {
501
+ let count = 0;
502
+ const spy = jest.fn(() => {
503
+ count++;
504
+ return count;
505
+ });
506
+ const data = {
507
+ answer: spy
508
+ };
509
+ const query = {
510
+ x: {
511
+ resource: 'answer'
512
+ }
513
+ };
514
+
515
+ const wrapper = ({
516
+ children
517
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
518
+ data: data
519
+ }, children);
520
+
521
+ const {
522
+ result,
523
+ waitFor
524
+ } = renderHook(() => useDataQuery(query, {
525
+ lazy: true
526
+ }), {
527
+ wrapper
528
+ });
529
+ expect(spy).not.toHaveBeenCalled();
530
+ const initialRefetch = result.current.refetch;
531
+ act(() => {
532
+ initialRefetch();
533
+ });
534
+ await waitFor(() => {
535
+ expect(result.current).toMatchObject({
536
+ loading: false,
537
+ called: true,
538
+ data: {
539
+ x: 1
540
+ }
541
+ });
542
+ });
543
+ expect(spy).toHaveBeenCalledTimes(1);
544
+ act(() => {
545
+ initialRefetch();
546
+ });
547
+ await waitFor(() => {
548
+ expect(result.current).toMatchObject({
549
+ loading: false,
550
+ called: true,
551
+ data: {
552
+ x: 2
553
+ }
554
+ });
555
+ });
556
+ expect(spy).toHaveBeenCalledTimes(2);
557
+ expect(initialRefetch).toBe(result.current.refetch);
558
+ });
500
559
  it('Should only trigger a single request when refetch is called on a lazy query with new variables', async () => {
501
560
  const spy = jest.fn((type, query) => {
502
561
  if (query.id === '1') {
@@ -1,10 +1,11 @@
1
- import { useState, useEffect, useRef } from 'react';
1
+ import { useState, useEffect, useRef, useDebugValue } from 'react';
2
2
  export const useStaticInput = (staticValue, {
3
3
  warn = false,
4
4
  name = 'input'
5
5
  } = {}) => {
6
6
  const originalValue = useRef(staticValue);
7
7
  const [value, setValue] = useState(() => originalValue.current);
8
+ useDebugValue(value, debugValue => "".concat(name, ": ").concat(JSON.stringify(debugValue)));
8
9
  useEffect(() => {
9
10
  if (warn && originalValue.current !== staticValue) {
10
11
  console.warn("The ".concat(name, " should be static, don't create it within the render loop!"));
@@ -1,2 +1,3 @@
1
1
  import { ResolvedResourceQuery, FetchType } from '../../engine';
2
- export declare const queryToResourcePath: (apiPath: string, query: ResolvedResourceQuery, type: FetchType) => string;
2
+ import { RestAPILink } from '../RestAPILink';
3
+ export declare const queryToResourcePath: (link: RestAPILink, query: ResolvedResourceQuery, type: FetchType) => string;
@@ -1,13 +1,10 @@
1
+ import type { Config } from '@dhis2/app-service-config';
1
2
  import { DataEngineLink, DataEngineLinkExecuteOptions, FetchType, JsonValue, ResolvedResourceQuery } from '../engine/';
2
- export interface RestAPILinkInput {
3
- baseUrl: string;
4
- apiVersion: number;
5
- }
6
3
  export declare class RestAPILink implements DataEngineLink {
7
- private apiPath;
8
- private baseUrl;
9
- private apiVersion;
10
- constructor({ baseUrl, apiVersion }: RestAPILinkInput);
4
+ readonly config: Config;
5
+ readonly versionedApiPath: string;
6
+ readonly unversionedApiPath: string;
7
+ constructor(config: Config);
11
8
  private fetch;
12
9
  executeResourceQuery(type: FetchType, query: ResolvedResourceQuery, { signal }: DataEngineLinkExecuteOptions): Promise<JsonValue>;
13
10
  }
@@ -0,0 +1,6 @@
1
+ import type { QueryVariables } from '../../engine';
2
+ export declare const mergeAndCompareVariables: (previousVariables?: QueryVariables | undefined, newVariables?: QueryVariables | undefined, previousHash?: string | undefined) => {
3
+ identical: boolean;
4
+ mergedVariablesHash: string | undefined;
5
+ mergedVariables: QueryVariables | undefined;
6
+ };
@@ -1,3 +1,3 @@
1
- import { Query, QueryOptions } from '../../engine';
2
- import { QueryRenderInput } from '../../types';
1
+ import type { Query, QueryOptions } from '../../engine';
2
+ import type { QueryRenderInput } from '../../types';
3
3
  export declare const useDataQuery: (query: Query, { onComplete: userOnSuccess, onError: userOnError, variables: initialVariables, lazy: initialLazy, }?: QueryOptions) => QueryRenderInput;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2/app-service-data",
3
- "version": "3.4.0",
3
+ "version": "3.4.3",
4
4
  "main": "./build/cjs/index.js",
5
5
  "module": "./build/es/index.js",
6
6
  "types": "build/types/index.d.ts",
@@ -22,7 +22,7 @@
22
22
  "build/**"
23
23
  ],
24
24
  "peerDependencies": {
25
- "@dhis2/app-service-config": "3.4.0",
25
+ "@dhis2/app-service-config": "3.4.3",
26
26
  "@dhis2/cli-app-scripts": "^7.1.1",
27
27
  "prop-types": "^15.7.2",
28
28
  "react": "^16.8",