@dhis2/app-service-data 3.2.3 → 3.2.7

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.
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isMetadataPackageInstallation = exports.addOrUpdateConfigurationProperty = exports.isAddOrUpdateSystemOrUserSetting = exports.isInterpretationCommentUpdate = exports.isCommentOnInterpretation = exports.isCreateOrUpdateInterpretation = exports.isCreateFeedbackMessage = exports.isReplyToMessageConversation = void 0;
6
+ exports.isMetadataPackageInstallation = exports.addOrUpdateConfigurationProperty = exports.isAddOrUpdateSystemOrUserSetting = exports.isInterpretationCommentUpdate = exports.isCommentOnInterpretation = exports.isUpdateInterpretation = exports.isCreateInterpretation = exports.isCreateFeedbackMessage = exports.isReplyToMessageConversation = void 0;
7
7
 
8
8
  /*
9
9
  * Requests that expect a "text/plain" Content-Type have been collected by scanning
@@ -28,33 +28,43 @@ exports.isReplyToMessageConversation = isReplyToMessageConversation;
28
28
 
29
29
  const isCreateFeedbackMessage = (type, {
30
30
  resource
31
- }) => type === 'create' && resource === 'messageConversations/feedback'; // POST or PUT to `interpretations/${objectType}/${id}` (add or update an interpretation)
31
+ }) => type === 'create' && resource === 'messageConversations/feedback'; // POST `interpretations/${objectType}/${id}` (add an interpretation to a visualization)
32
32
 
33
33
 
34
34
  exports.isCreateFeedbackMessage = isCreateFeedbackMessage;
35
35
 
36
- const isCreateOrUpdateInterpretation = (type, {
36
+ const isCreateInterpretation = (type, {
37
+ resource
38
+ }) => {
39
+ const pattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventReport|eventChart|dataSetReport)\/[a-zA-Z0-9]{11}$/;
40
+ return type === 'create' && pattern.test(resource);
41
+ }; // PUT to `interpretations/${id}` (update an interpretation)
42
+
43
+
44
+ exports.isCreateInterpretation = isCreateInterpretation;
45
+
46
+ const isUpdateInterpretation = (type, {
37
47
  resource,
38
48
  id
39
49
  }) => {
40
- if (type !== 'create' && type !== 'replace') {
50
+ if (type !== 'replace') {
41
51
  return false;
42
52
  }
43
53
 
44
54
  let resourcePattern;
45
55
 
46
- if (type === 'replace' && id) {
47
- resourcePattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventReport|eventChart|dataSetReport)$/;
56
+ if (id) {
57
+ resourcePattern = /^interpretations$/;
48
58
  const idPattern = /^[a-zA-Z0-9]{11}$/;
49
59
  return resourcePattern.test(resource) && idPattern.test(id);
50
60
  }
51
61
 
52
- resourcePattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventReport|eventChart|dataSetReport)\/[a-zA-Z0-9]{11}$/;
62
+ resourcePattern = /^interpretations\/[a-zA-Z0-9]{11}$/;
53
63
  return resourcePattern.test(resource);
54
64
  }; // POST to `interpretations/${id}/comments` (comment on an interpretation)
55
65
 
56
66
 
57
- exports.isCreateOrUpdateInterpretation = isCreateOrUpdateInterpretation;
67
+ exports.isUpdateInterpretation = isUpdateInterpretation;
58
68
 
59
69
  const isCommentOnInterpretation = (type, {
60
70
  resource
@@ -26,30 +26,58 @@ describe('isCreateFeedbackMessage', () => {
26
26
  })).toEqual(false);
27
27
  });
28
28
  });
29
- describe('isCreateOrUpdateInterpretation', () => {
29
+ describe('isCreateInterpretation', () => {
30
30
  it('returns true for a POST to "interpretations/chart/${id}"', () => {
31
- expect((0, _textPlainMatchers.isCreateOrUpdateInterpretation)('create', {
31
+ expect((0, _textPlainMatchers.isCreateInterpretation)('create', {
32
32
  resource: 'interpretations/chart/oXD88WWSQpR'
33
33
  })).toEqual(true);
34
34
  });
35
- it('returns true for a PUT to "interpretations/chart/${id}"', () => {
36
- expect((0, _textPlainMatchers.isCreateOrUpdateInterpretation)('replace', {
35
+ it('returns false for a PUT to "interpretations/chart/${id}"', () => {
36
+ expect((0, _textPlainMatchers.isCreateInterpretation)('replace', {
37
37
  resource: 'interpretations/chart/oXD88WWSQpR'
38
+ })).toEqual(false);
39
+ });
40
+ it('retuns false for PATCH requests with a valid query', () => {
41
+ expect((0, _textPlainMatchers.isCreateInterpretation)('update', {
42
+ resource: 'interpretations/chart/oXD88WWSQpR'
43
+ })).toEqual(false);
44
+ });
45
+ it('returns false for a request to a different resource', () => {
46
+ expect((0, _textPlainMatchers.isCreateInterpretation)('create', {
47
+ resource: 'interpretations/dummy/oXD88WWSQpR'
48
+ })).toEqual(false);
49
+ });
50
+ });
51
+ describe('isUpdateInterpretation', () => {
52
+ it('returns true for a PUT to "interpretations/${id}"', () => {
53
+ expect((0, _textPlainMatchers.isUpdateInterpretation)('replace', {
54
+ resource: 'interpretations/oXD88WWSQpR'
38
55
  })).toEqual(true);
39
56
  });
40
57
  it('returns true for PUT with populated query.id', () => {
41
- expect((0, _textPlainMatchers.isCreateOrUpdateInterpretation)('replace', {
42
- resource: 'interpretations/chart',
58
+ expect((0, _textPlainMatchers.isUpdateInterpretation)('replace', {
59
+ resource: 'interpretations',
43
60
  id: 'oXD88WWSQpR'
44
61
  })).toEqual(true);
45
62
  });
46
- it('retuns false for PATCH requests with a valid query', () => {
47
- expect((0, _textPlainMatchers.isCreateOrUpdateInterpretation)('update', {
48
- resource: 'interpretations/chart/oXD88WWSQpR'
63
+ it('returns false for a POST to "interpretations/${id}"', () => {
64
+ expect((0, _textPlainMatchers.isUpdateInterpretation)('create', {
65
+ resource: 'interpretations/oXD88WWSQpR'
66
+ })).toEqual(false);
67
+ });
68
+ it('returns false for a PATCH to "interpretations/${id}"', () => {
69
+ expect((0, _textPlainMatchers.isUpdateInterpretation)('update', {
70
+ resource: 'interpretations/oXD88WWSQpR'
71
+ })).toEqual(false);
72
+ });
73
+ it('returns false for PATCH with populated query.id', () => {
74
+ expect((0, _textPlainMatchers.isUpdateInterpretation)('update', {
75
+ resource: 'interpretations',
76
+ id: 'oXD88WWSQpR'
49
77
  })).toEqual(false);
50
78
  });
51
79
  it('returns false for a request to a different resource', () => {
52
- expect((0, _textPlainMatchers.isCreateOrUpdateInterpretation)('create', {
80
+ expect((0, _textPlainMatchers.isUpdateInterpretation)('create', {
53
81
  resource: 'interpretations/dummy/oXD88WWSQpR'
54
82
  })).toEqual(false);
55
83
  });
@@ -96,9 +96,13 @@ const useDataQuery = (query, {
96
96
  /**
97
97
  * Refetch allows a user to update the variables or just
98
98
  * trigger a refetch of the query with the current variables.
99
+ *
100
+ * We're using useCallback to make the identity of the function
101
+ * as stable as possible, so that it won't trigger excessive
102
+ * rerenders when used for side-effects.
99
103
  */
100
104
 
101
- const refetch = newVariables => {
105
+ const refetch = (0, _react.useCallback)(newVariables => {
102
106
  /**
103
107
  * If there are no updates that will trigger an automatic refetch
104
108
  * we'll need to call react-query's refetch directly
@@ -112,10 +116,6 @@ const useDataQuery = (query, {
112
116
  }) => data);
113
117
  }
114
118
 
115
- if (!enabled) {
116
- setEnabled(true);
117
- }
118
-
119
119
  if (newVariables) {
120
120
  // Use cached hash if it exists
121
121
  const currentHash = variablesHash.current || (0, _stableVariablesHash.stableVariablesHash)(variables);
@@ -125,8 +125,11 @@ const useDataQuery = (query, {
125
125
  const mergedHash = (0, _stableVariablesHash.stableVariablesHash)(mergedVariables);
126
126
  const identical = currentHash === mergedHash;
127
127
 
128
- if (identical) {
129
- // If the variables are identical we'll need to trigger the refetch manually
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
+ */
130
133
  return queryRefetch({
131
134
  cancelRefetch: true,
132
135
  throwOnError: false
@@ -137,6 +140,11 @@ const useDataQuery = (query, {
137
140
  variablesHash.current = mergedHash;
138
141
  setVariables(mergedVariables);
139
142
  }
143
+ } // Enable the query after the variables have been set to prevent extra request
144
+
145
+
146
+ if (!enabled) {
147
+ setEnabled(true);
140
148
  } // This promise does not currently reject on errors
141
149
 
142
150
 
@@ -145,13 +153,12 @@ const useDataQuery = (query, {
145
153
  resolve(data);
146
154
  };
147
155
  });
148
- };
156
+ }, [enabled, queryRefetch, variables]);
149
157
  /**
150
158
  * react-query returns null or an error, but we return undefined
151
159
  * or an error, so this ensures consistency with the other types.
152
160
  */
153
161
 
154
-
155
162
  const ourError = error || undefined;
156
163
  return {
157
164
  engine,
@@ -507,6 +507,149 @@ describe('useDataQuery', () => {
507
507
  });
508
508
  });
509
509
  describe('return values: refetch', () => {
510
+ it('Should only trigger a single request when refetch is called on a lazy query with new variables', async () => {
511
+ const spy = jest.fn((type, query) => {
512
+ if (query.id === '1') {
513
+ return 42;
514
+ }
515
+
516
+ return 0;
517
+ });
518
+ const data = {
519
+ answer: spy
520
+ };
521
+ const query = {
522
+ x: {
523
+ resource: 'answer',
524
+ id: ({
525
+ id
526
+ }) => id
527
+ }
528
+ };
529
+
530
+ const wrapper = ({
531
+ children
532
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
533
+ data: data
534
+ }, children);
535
+
536
+ const {
537
+ result,
538
+ waitFor
539
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataQuery.useDataQuery)(query, {
540
+ lazy: true
541
+ }), {
542
+ wrapper
543
+ });
544
+ expect(spy).not.toHaveBeenCalled();
545
+ (0, _reactHooks.act)(() => {
546
+ result.current.refetch({
547
+ id: '1'
548
+ });
549
+ });
550
+ await waitFor(() => {
551
+ expect(result.current).toMatchObject({
552
+ loading: false,
553
+ called: true,
554
+ data: {
555
+ x: 42
556
+ }
557
+ });
558
+ });
559
+ expect(spy).toHaveBeenCalledTimes(1);
560
+ });
561
+ it('Should only trigger a single request when refetch is called on a lazy query with identical variables', async () => {
562
+ const spy = jest.fn((type, query) => {
563
+ if (query.id === '1') {
564
+ return 42;
565
+ }
566
+
567
+ return 0;
568
+ });
569
+ const data = {
570
+ answer: spy
571
+ };
572
+ const query = {
573
+ x: {
574
+ resource: 'answer',
575
+ id: ({
576
+ id
577
+ }) => id
578
+ }
579
+ };
580
+
581
+ const wrapper = ({
582
+ children
583
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
584
+ data: data
585
+ }, children);
586
+
587
+ const {
588
+ result,
589
+ waitFor
590
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataQuery.useDataQuery)(query, {
591
+ lazy: true,
592
+ variables: {
593
+ id: '1'
594
+ }
595
+ }), {
596
+ wrapper
597
+ });
598
+ expect(spy).not.toHaveBeenCalled();
599
+ (0, _reactHooks.act)(() => {
600
+ result.current.refetch({
601
+ id: '1'
602
+ });
603
+ });
604
+ await waitFor(() => {
605
+ expect(result.current).toMatchObject({
606
+ loading: false,
607
+ called: true,
608
+ data: {
609
+ x: 42
610
+ }
611
+ });
612
+ });
613
+ expect(spy).toHaveBeenCalledTimes(1);
614
+ });
615
+ it('Should have a stable identity if the variables have not changed', async () => {
616
+ const data = {
617
+ answer: () => 42
618
+ };
619
+ const query = {
620
+ x: {
621
+ resource: 'answer'
622
+ }
623
+ };
624
+
625
+ const wrapper = ({
626
+ children
627
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
628
+ data: data
629
+ }, children);
630
+
631
+ const {
632
+ result,
633
+ waitForNextUpdate,
634
+ rerender
635
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataQuery.useDataQuery)(query), {
636
+ wrapper
637
+ });
638
+ const firstRefetch = result.current.refetch;
639
+ await waitForNextUpdate();
640
+ (0, _reactHooks.act)(() => {
641
+ result.current.refetch();
642
+ /**
643
+ * FIXME: https://github.com/tannerlinsley/react-query/issues/2481
644
+ * This forced rerender is not necessary in the app, just when testing.
645
+ * It is unclear why.
646
+ */
647
+
648
+ rerender();
649
+ });
650
+ await waitForNextUpdate();
651
+ expect(result.current.refetch).toBe(firstRefetch);
652
+ });
510
653
  it('Should return stale data and set loading to true on refetch', async () => {
511
654
  const answers = [42, 43];
512
655
  const mockSpy = jest.fn(() => Promise.resolve(answers.shift()));
@@ -18,25 +18,32 @@ export const isReplyToMessageConversation = (type, {
18
18
 
19
19
  export const isCreateFeedbackMessage = (type, {
20
20
  resource
21
- }) => type === 'create' && resource === 'messageConversations/feedback'; // POST or PUT to `interpretations/${objectType}/${id}` (add or update an interpretation)
21
+ }) => type === 'create' && resource === 'messageConversations/feedback'; // POST `interpretations/${objectType}/${id}` (add an interpretation to a visualization)
22
22
 
23
- export const isCreateOrUpdateInterpretation = (type, {
23
+ export const isCreateInterpretation = (type, {
24
+ resource
25
+ }) => {
26
+ const pattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventReport|eventChart|dataSetReport)\/[a-zA-Z0-9]{11}$/;
27
+ return type === 'create' && pattern.test(resource);
28
+ }; // PUT to `interpretations/${id}` (update an interpretation)
29
+
30
+ export const isUpdateInterpretation = (type, {
24
31
  resource,
25
32
  id
26
33
  }) => {
27
- if (type !== 'create' && type !== 'replace') {
34
+ if (type !== 'replace') {
28
35
  return false;
29
36
  }
30
37
 
31
38
  let resourcePattern;
32
39
 
33
- if (type === 'replace' && id) {
34
- resourcePattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventReport|eventChart|dataSetReport)$/;
40
+ if (id) {
41
+ resourcePattern = /^interpretations$/;
35
42
  const idPattern = /^[a-zA-Z0-9]{11}$/;
36
43
  return resourcePattern.test(resource) && idPattern.test(id);
37
44
  }
38
45
 
39
- resourcePattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventReport|eventChart|dataSetReport)\/[a-zA-Z0-9]{11}$/;
46
+ resourcePattern = /^interpretations\/[a-zA-Z0-9]{11}$/;
40
47
  return resourcePattern.test(resource);
41
48
  }; // POST to `interpretations/${id}/comments` (comment on an interpretation)
42
49
 
@@ -1,4 +1,4 @@
1
- import { isReplyToMessageConversation, isCreateFeedbackMessage, isCreateOrUpdateInterpretation, isCommentOnInterpretation, isInterpretationCommentUpdate, isAddOrUpdateSystemOrUserSetting, addOrUpdateConfigurationProperty, isMetadataPackageInstallation } from './textPlainMatchers';
1
+ import { isReplyToMessageConversation, isCreateFeedbackMessage, isCreateInterpretation, isUpdateInterpretation, isCommentOnInterpretation, isInterpretationCommentUpdate, isAddOrUpdateSystemOrUserSetting, addOrUpdateConfigurationProperty, isMetadataPackageInstallation } from './textPlainMatchers';
2
2
  describe('isReplyToMessageConversation', () => {
3
3
  it('retuns true for POST to `messageConversations/${id}`', () => {
4
4
  expect(isReplyToMessageConversation('create', {
@@ -23,30 +23,58 @@ describe('isCreateFeedbackMessage', () => {
23
23
  })).toEqual(false);
24
24
  });
25
25
  });
26
- describe('isCreateOrUpdateInterpretation', () => {
26
+ describe('isCreateInterpretation', () => {
27
27
  it('returns true for a POST to "interpretations/chart/${id}"', () => {
28
- expect(isCreateOrUpdateInterpretation('create', {
28
+ expect(isCreateInterpretation('create', {
29
29
  resource: 'interpretations/chart/oXD88WWSQpR'
30
30
  })).toEqual(true);
31
31
  });
32
- it('returns true for a PUT to "interpretations/chart/${id}"', () => {
33
- expect(isCreateOrUpdateInterpretation('replace', {
32
+ it('returns false for a PUT to "interpretations/chart/${id}"', () => {
33
+ expect(isCreateInterpretation('replace', {
34
34
  resource: 'interpretations/chart/oXD88WWSQpR'
35
+ })).toEqual(false);
36
+ });
37
+ it('retuns false for PATCH requests with a valid query', () => {
38
+ expect(isCreateInterpretation('update', {
39
+ resource: 'interpretations/chart/oXD88WWSQpR'
40
+ })).toEqual(false);
41
+ });
42
+ it('returns false for a request to a different resource', () => {
43
+ expect(isCreateInterpretation('create', {
44
+ resource: 'interpretations/dummy/oXD88WWSQpR'
45
+ })).toEqual(false);
46
+ });
47
+ });
48
+ describe('isUpdateInterpretation', () => {
49
+ it('returns true for a PUT to "interpretations/${id}"', () => {
50
+ expect(isUpdateInterpretation('replace', {
51
+ resource: 'interpretations/oXD88WWSQpR'
35
52
  })).toEqual(true);
36
53
  });
37
54
  it('returns true for PUT with populated query.id', () => {
38
- expect(isCreateOrUpdateInterpretation('replace', {
39
- resource: 'interpretations/chart',
55
+ expect(isUpdateInterpretation('replace', {
56
+ resource: 'interpretations',
40
57
  id: 'oXD88WWSQpR'
41
58
  })).toEqual(true);
42
59
  });
43
- it('retuns false for PATCH requests with a valid query', () => {
44
- expect(isCreateOrUpdateInterpretation('update', {
45
- resource: 'interpretations/chart/oXD88WWSQpR'
60
+ it('returns false for a POST to "interpretations/${id}"', () => {
61
+ expect(isUpdateInterpretation('create', {
62
+ resource: 'interpretations/oXD88WWSQpR'
63
+ })).toEqual(false);
64
+ });
65
+ it('returns false for a PATCH to "interpretations/${id}"', () => {
66
+ expect(isUpdateInterpretation('update', {
67
+ resource: 'interpretations/oXD88WWSQpR'
68
+ })).toEqual(false);
69
+ });
70
+ it('returns false for PATCH with populated query.id', () => {
71
+ expect(isUpdateInterpretation('update', {
72
+ resource: 'interpretations',
73
+ id: 'oXD88WWSQpR'
46
74
  })).toEqual(false);
47
75
  });
48
76
  it('returns false for a request to a different resource', () => {
49
- expect(isCreateOrUpdateInterpretation('create', {
77
+ expect(isUpdateInterpretation('create', {
50
78
  resource: 'interpretations/dummy/oXD88WWSQpR'
51
79
  })).toEqual(false);
52
80
  });
@@ -1,4 +1,4 @@
1
- import { useState, useRef } from 'react';
1
+ import { useState, useRef, useCallback } from 'react';
2
2
  import { useQuery, setLogger } from 'react-query';
3
3
  import { stableVariablesHash } from './stableVariablesHash';
4
4
  import { useDataEngine } from './useDataEngine';
@@ -84,9 +84,13 @@ export const useDataQuery = (query, {
84
84
  /**
85
85
  * Refetch allows a user to update the variables or just
86
86
  * trigger a refetch of the query with the current variables.
87
+ *
88
+ * We're using useCallback to make the identity of the function
89
+ * as stable as possible, so that it won't trigger excessive
90
+ * rerenders when used for side-effects.
87
91
  */
88
92
 
89
- const refetch = newVariables => {
93
+ const refetch = useCallback(newVariables => {
90
94
  /**
91
95
  * If there are no updates that will trigger an automatic refetch
92
96
  * we'll need to call react-query's refetch directly
@@ -100,10 +104,6 @@ export const useDataQuery = (query, {
100
104
  }) => data);
101
105
  }
102
106
 
103
- if (!enabled) {
104
- setEnabled(true);
105
- }
106
-
107
107
  if (newVariables) {
108
108
  // Use cached hash if it exists
109
109
  const currentHash = variablesHash.current || stableVariablesHash(variables);
@@ -113,8 +113,11 @@ export const useDataQuery = (query, {
113
113
  const mergedHash = stableVariablesHash(mergedVariables);
114
114
  const identical = currentHash === mergedHash;
115
115
 
116
- if (identical) {
117
- // If the variables are identical we'll need to trigger the refetch manually
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
+ */
118
121
  return queryRefetch({
119
122
  cancelRefetch: true,
120
123
  throwOnError: false
@@ -125,6 +128,11 @@ export const useDataQuery = (query, {
125
128
  variablesHash.current = mergedHash;
126
129
  setVariables(mergedVariables);
127
130
  }
131
+ } // Enable the query after the variables have been set to prevent extra request
132
+
133
+
134
+ if (!enabled) {
135
+ setEnabled(true);
128
136
  } // This promise does not currently reject on errors
129
137
 
130
138
 
@@ -133,13 +141,12 @@ export const useDataQuery = (query, {
133
141
  resolve(data);
134
142
  };
135
143
  });
136
- };
144
+ }, [enabled, queryRefetch, variables]);
137
145
  /**
138
146
  * react-query returns null or an error, but we return undefined
139
147
  * or an error, so this ensures consistency with the other types.
140
148
  */
141
149
 
142
-
143
150
  const ourError = error || undefined;
144
151
  return {
145
152
  engine,
@@ -497,6 +497,149 @@ describe('useDataQuery', () => {
497
497
  });
498
498
  });
499
499
  describe('return values: refetch', () => {
500
+ it('Should only trigger a single request when refetch is called on a lazy query with new variables', async () => {
501
+ const spy = jest.fn((type, query) => {
502
+ if (query.id === '1') {
503
+ return 42;
504
+ }
505
+
506
+ return 0;
507
+ });
508
+ const data = {
509
+ answer: spy
510
+ };
511
+ const query = {
512
+ x: {
513
+ resource: 'answer',
514
+ id: ({
515
+ id
516
+ }) => id
517
+ }
518
+ };
519
+
520
+ const wrapper = ({
521
+ children
522
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
523
+ data: data
524
+ }, children);
525
+
526
+ const {
527
+ result,
528
+ waitFor
529
+ } = renderHook(() => useDataQuery(query, {
530
+ lazy: true
531
+ }), {
532
+ wrapper
533
+ });
534
+ expect(spy).not.toHaveBeenCalled();
535
+ act(() => {
536
+ result.current.refetch({
537
+ id: '1'
538
+ });
539
+ });
540
+ await waitFor(() => {
541
+ expect(result.current).toMatchObject({
542
+ loading: false,
543
+ called: true,
544
+ data: {
545
+ x: 42
546
+ }
547
+ });
548
+ });
549
+ expect(spy).toHaveBeenCalledTimes(1);
550
+ });
551
+ it('Should only trigger a single request when refetch is called on a lazy query with identical variables', async () => {
552
+ const spy = jest.fn((type, query) => {
553
+ if (query.id === '1') {
554
+ return 42;
555
+ }
556
+
557
+ return 0;
558
+ });
559
+ const data = {
560
+ answer: spy
561
+ };
562
+ const query = {
563
+ x: {
564
+ resource: 'answer',
565
+ id: ({
566
+ id
567
+ }) => id
568
+ }
569
+ };
570
+
571
+ const wrapper = ({
572
+ children
573
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
574
+ data: data
575
+ }, children);
576
+
577
+ const {
578
+ result,
579
+ waitFor
580
+ } = renderHook(() => useDataQuery(query, {
581
+ lazy: true,
582
+ variables: {
583
+ id: '1'
584
+ }
585
+ }), {
586
+ wrapper
587
+ });
588
+ expect(spy).not.toHaveBeenCalled();
589
+ act(() => {
590
+ result.current.refetch({
591
+ id: '1'
592
+ });
593
+ });
594
+ await waitFor(() => {
595
+ expect(result.current).toMatchObject({
596
+ loading: false,
597
+ called: true,
598
+ data: {
599
+ x: 42
600
+ }
601
+ });
602
+ });
603
+ expect(spy).toHaveBeenCalledTimes(1);
604
+ });
605
+ it('Should have a stable identity if the variables have not changed', async () => {
606
+ const data = {
607
+ answer: () => 42
608
+ };
609
+ const query = {
610
+ x: {
611
+ resource: 'answer'
612
+ }
613
+ };
614
+
615
+ const wrapper = ({
616
+ children
617
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
618
+ data: data
619
+ }, children);
620
+
621
+ const {
622
+ result,
623
+ waitForNextUpdate,
624
+ rerender
625
+ } = renderHook(() => useDataQuery(query), {
626
+ wrapper
627
+ });
628
+ const firstRefetch = result.current.refetch;
629
+ await waitForNextUpdate();
630
+ act(() => {
631
+ result.current.refetch();
632
+ /**
633
+ * FIXME: https://github.com/tannerlinsley/react-query/issues/2481
634
+ * This forced rerender is not necessary in the app, just when testing.
635
+ * It is unclear why.
636
+ */
637
+
638
+ rerender();
639
+ });
640
+ await waitForNextUpdate();
641
+ expect(result.current.refetch).toBe(firstRefetch);
642
+ });
500
643
  it('Should return stale data and set loading to true on refetch', async () => {
501
644
  const answers = [42, 43];
502
645
  const mockSpy = jest.fn(() => Promise.resolve(answers.shift()));
@@ -1,7 +1,8 @@
1
1
  import { ResolvedResourceQuery, FetchType } from '../../../engine';
2
2
  export declare const isReplyToMessageConversation: (type: FetchType, { resource }: ResolvedResourceQuery) => boolean;
3
3
  export declare const isCreateFeedbackMessage: (type: FetchType, { resource }: ResolvedResourceQuery) => boolean;
4
- export declare const isCreateOrUpdateInterpretation: (type: FetchType, { resource, id }: ResolvedResourceQuery) => boolean;
4
+ export declare const isCreateInterpretation: (type: FetchType, { resource }: ResolvedResourceQuery) => boolean;
5
+ export declare const isUpdateInterpretation: (type: FetchType, { resource, id }: ResolvedResourceQuery) => boolean;
5
6
  export declare const isCommentOnInterpretation: (type: FetchType, { resource }: ResolvedResourceQuery) => boolean;
6
7
  export declare const isInterpretationCommentUpdate: (type: FetchType, { resource, id }: ResolvedResourceQuery) => boolean;
7
8
  export declare const isAddOrUpdateSystemOrUserSetting: (type: FetchType, { resource }: ResolvedResourceQuery) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2/app-service-data",
3
- "version": "3.2.3",
3
+ "version": "3.2.7",
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.2.3",
25
+ "@dhis2/app-service-config": "3.2.7",
26
26
  "@dhis2/cli-app-scripts": "^7.1.1",
27
27
  "prop-types": "^15.7.2",
28
28
  "react": "^16.8",