@dhis2/app-service-data 3.2.2 → 3.2.6

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
@@ -145,13 +149,12 @@ const useDataQuery = (query, {
145
149
  resolve(data);
146
150
  };
147
151
  });
148
- };
152
+ }, [enabled, queryRefetch, variables]);
149
153
  /**
150
154
  * react-query returns null or an error, but we return undefined
151
155
  * or an error, so this ensures consistency with the other types.
152
156
  */
153
157
 
154
-
155
158
  const ourError = error || undefined;
156
159
  return {
157
160
  engine,
@@ -507,6 +507,44 @@ describe('useDataQuery', () => {
507
507
  });
508
508
  });
509
509
  describe('return values: refetch', () => {
510
+ it('Should have a stable identity if the variables have not changed', async () => {
511
+ const data = {
512
+ answer: () => 42
513
+ };
514
+ const query = {
515
+ x: {
516
+ resource: 'answer'
517
+ }
518
+ };
519
+
520
+ const wrapper = ({
521
+ children
522
+ }) => /*#__PURE__*/React.createElement(_CustomDataProvider.CustomDataProvider, {
523
+ data: data
524
+ }, children);
525
+
526
+ const {
527
+ result,
528
+ waitForNextUpdate,
529
+ rerender
530
+ } = (0, _reactHooks.renderHook)(() => (0, _useDataQuery.useDataQuery)(query), {
531
+ wrapper
532
+ });
533
+ const firstRefetch = result.current.refetch;
534
+ await waitForNextUpdate();
535
+ (0, _reactHooks.act)(() => {
536
+ result.current.refetch();
537
+ /**
538
+ * FIXME: https://github.com/tannerlinsley/react-query/issues/2481
539
+ * This forced rerender is not necessary in the app, just when testing.
540
+ * It is unclear why.
541
+ */
542
+
543
+ rerender();
544
+ });
545
+ await waitForNextUpdate();
546
+ expect(result.current.refetch).toBe(firstRefetch);
547
+ });
510
548
  it('Should return stale data and set loading to true on refetch', async () => {
511
549
  const answers = [42, 43];
512
550
  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
@@ -133,13 +137,12 @@ export const useDataQuery = (query, {
133
137
  resolve(data);
134
138
  };
135
139
  });
136
- };
140
+ }, [enabled, queryRefetch, variables]);
137
141
  /**
138
142
  * react-query returns null or an error, but we return undefined
139
143
  * or an error, so this ensures consistency with the other types.
140
144
  */
141
145
 
142
-
143
146
  const ourError = error || undefined;
144
147
  return {
145
148
  engine,
@@ -497,6 +497,44 @@ describe('useDataQuery', () => {
497
497
  });
498
498
  });
499
499
  describe('return values: refetch', () => {
500
+ it('Should have a stable identity if the variables have not changed', async () => {
501
+ const data = {
502
+ answer: () => 42
503
+ };
504
+ const query = {
505
+ x: {
506
+ resource: 'answer'
507
+ }
508
+ };
509
+
510
+ const wrapper = ({
511
+ children
512
+ }) => /*#__PURE__*/React.createElement(CustomDataProvider, {
513
+ data: data
514
+ }, children);
515
+
516
+ const {
517
+ result,
518
+ waitForNextUpdate,
519
+ rerender
520
+ } = renderHook(() => useDataQuery(query), {
521
+ wrapper
522
+ });
523
+ const firstRefetch = result.current.refetch;
524
+ await waitForNextUpdate();
525
+ act(() => {
526
+ result.current.refetch();
527
+ /**
528
+ * FIXME: https://github.com/tannerlinsley/react-query/issues/2481
529
+ * This forced rerender is not necessary in the app, just when testing.
530
+ * It is unclear why.
531
+ */
532
+
533
+ rerender();
534
+ });
535
+ await waitForNextUpdate();
536
+ expect(result.current.refetch).toBe(firstRefetch);
537
+ });
500
538
  it('Should return stale data and set loading to true on refetch', async () => {
501
539
  const answers = [42, 43];
502
540
  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.2",
3
+ "version": "3.2.6",
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.2",
25
+ "@dhis2/app-service-config": "3.2.6",
26
26
  "@dhis2/cli-app-scripts": "^7.1.1",
27
27
  "prop-types": "^15.7.2",
28
28
  "react": "^16.8",