@dhis2/app-service-data 3.16.0 → 3.17.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/build/cjs/__tests__/integration.test.js +10 -16
  2. package/build/cjs/__tests__/mutations.test.js +5 -8
  3. package/build/cjs/index.js +16 -44
  4. package/build/cjs/react/components/CustomDataProvider.js +9 -11
  5. package/build/cjs/react/components/DataMutation.js +7 -8
  6. package/build/cjs/react/components/DataProvider.js +3 -4
  7. package/build/cjs/react/components/DataProvider.test.js +3 -4
  8. package/build/cjs/react/components/DataQuery.js +8 -9
  9. package/build/cjs/react/components/index.js +33 -0
  10. package/build/cjs/react/context/DataContext.js +2 -2
  11. package/build/cjs/react/context/defaultDataContext.js +13 -0
  12. package/build/cjs/react/context/{defaultContext.test.js → defaultDataContext.test.js} +3 -3
  13. package/build/cjs/react/hooks/index.js +26 -0
  14. package/build/cjs/react/hooks/useDataMutation.js +6 -7
  15. package/build/cjs/react/hooks/useDataMutation.test.js +44 -71
  16. package/build/cjs/react/hooks/useDataQuery.js +10 -14
  17. package/build/cjs/react/hooks/useDataQuery.test.js +172 -265
  18. package/build/cjs/react/hooks/useQueryExecutor.js +11 -13
  19. package/build/cjs/react/hooks/useQueryExecutor.test.js +12 -16
  20. package/build/cjs/react/hooks/useStaticInput.js +4 -5
  21. package/build/cjs/react/hooks/useStaticInput.test.js +24 -39
  22. package/build/cjs/react/index.js +22 -77
  23. package/build/es/__tests__/integration.test.js +10 -16
  24. package/build/es/__tests__/mutations.test.js +5 -8
  25. package/build/es/index.js +2 -3
  26. package/build/es/react/components/CustomDataProvider.js +7 -9
  27. package/build/es/react/components/DataMutation.js +7 -8
  28. package/build/es/react/components/DataProvider.js +1 -2
  29. package/build/es/react/components/DataProvider.test.js +1 -2
  30. package/build/es/react/components/DataQuery.js +8 -9
  31. package/build/es/react/components/index.js +4 -0
  32. package/build/es/react/context/DataContext.js +2 -2
  33. package/build/es/react/context/{defaultContext.js → defaultDataContext.js} +2 -3
  34. package/build/es/react/context/{defaultContext.test.js → defaultDataContext.test.js} +3 -3
  35. package/build/es/react/hooks/index.js +3 -0
  36. package/build/es/react/hooks/useDataMutation.js +6 -7
  37. package/build/es/react/hooks/useDataMutation.test.js +44 -71
  38. package/build/es/react/hooks/useDataQuery.js +10 -14
  39. package/build/es/react/hooks/useDataQuery.test.js +172 -265
  40. package/build/es/react/hooks/useQueryExecutor.js +10 -12
  41. package/build/es/react/hooks/useQueryExecutor.test.js +12 -16
  42. package/build/es/react/hooks/useStaticInput.js +4 -5
  43. package/build/es/react/hooks/useStaticInput.test.js +24 -39
  44. package/build/es/react/index.js +3 -11
  45. package/build/types/index.d.ts +2 -3
  46. package/build/types/react/components/CustomDataProvider.d.ts +1 -1
  47. package/build/types/react/components/DataMutation.d.ts +1 -1
  48. package/build/types/react/components/DataQuery.d.ts +1 -1
  49. package/build/types/react/components/index.d.ts +4 -0
  50. package/build/types/react/context/defaultDataContext.d.ts +4 -0
  51. package/build/types/react/hooks/index.d.ts +3 -0
  52. package/build/types/react/hooks/mergeAndCompareVariables.d.ts +1 -1
  53. package/build/types/react/hooks/useDataEngine.d.ts +1 -1
  54. package/build/types/react/hooks/useDataMutation.d.ts +1 -1
  55. package/build/types/react/hooks/useDataQuery.d.ts +1 -1
  56. package/build/types/react/index.d.ts +2 -11
  57. package/build/types/types.d.ts +2 -7
  58. package/package.json +4 -3
  59. package/build/cjs/engine/DataEngine.js +0 -73
  60. package/build/cjs/engine/DataEngine.test.js +0 -156
  61. package/build/cjs/engine/helpers/getMutationFetchType.js +0 -8
  62. package/build/cjs/engine/helpers/getMutationFetchType.test.js +0 -39
  63. package/build/cjs/engine/helpers/resolveDynamicQuery.js +0 -21
  64. package/build/cjs/engine/helpers/resolveDynamicQuery.test.js +0 -63
  65. package/build/cjs/engine/helpers/validate.js +0 -62
  66. package/build/cjs/engine/helpers/validate.test.js +0 -206
  67. package/build/cjs/engine/index.js +0 -104
  68. package/build/cjs/engine/types/DataEngineLink.js +0 -1
  69. package/build/cjs/engine/types/ExecuteOptions.js +0 -1
  70. package/build/cjs/engine/types/FetchError.js +0 -24
  71. package/build/cjs/engine/types/FetchError.test.js +0 -14
  72. package/build/cjs/engine/types/InvalidQueryError.js +0 -18
  73. package/build/cjs/engine/types/JsonValue.js +0 -1
  74. package/build/cjs/engine/types/Mutation.js +0 -1
  75. package/build/cjs/engine/types/PossiblyDynamic.js +0 -1
  76. package/build/cjs/engine/types/Query.js +0 -1
  77. package/build/cjs/engine/types/QueryParameters.js +0 -1
  78. package/build/cjs/links/CustomDataLink.js +0 -51
  79. package/build/cjs/links/CustomDataLink.test.js +0 -73
  80. package/build/cjs/links/ErrorLink.js +0 -20
  81. package/build/cjs/links/RestAPILink/fetchData.js +0 -80
  82. package/build/cjs/links/RestAPILink/fetchData.test.js +0 -132
  83. package/build/cjs/links/RestAPILink/metadataResources.js +0 -22
  84. package/build/cjs/links/RestAPILink/path.js +0 -14
  85. package/build/cjs/links/RestAPILink/path.test.js +0 -16
  86. package/build/cjs/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.js +0 -58
  87. package/build/cjs/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.test.js +0 -73
  88. package/build/cjs/links/RestAPILink/queryToRequestOptions/requestContentType.js +0 -80
  89. package/build/cjs/links/RestAPILink/queryToRequestOptions/requestContentType.test.js +0 -120
  90. package/build/cjs/links/RestAPILink/queryToRequestOptions/textPlainMatchers.js +0 -170
  91. package/build/cjs/links/RestAPILink/queryToRequestOptions/textPlainMatchers.test.js +0 -246
  92. package/build/cjs/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.js +0 -14
  93. package/build/cjs/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.test.js +0 -20
  94. package/build/cjs/links/RestAPILink/queryToRequestOptions.js +0 -34
  95. package/build/cjs/links/RestAPILink/queryToRequestOptions.test.js +0 -107
  96. package/build/cjs/links/RestAPILink/queryToResourcePath.js +0 -82
  97. package/build/cjs/links/RestAPILink/queryToResourcePath.test.js +0 -173
  98. package/build/cjs/links/RestAPILink/validateQuery.js +0 -59
  99. package/build/cjs/links/RestAPILink/validateQuery.test.js +0 -209
  100. package/build/cjs/links/RestAPILink.js +0 -33
  101. package/build/cjs/links/RestAPILink.test.js +0 -21
  102. package/build/cjs/links/index.js +0 -38
  103. package/build/cjs/locales/en/translations.json +0 -3
  104. package/build/cjs/locales/index.js +0 -21
  105. package/build/cjs/react/context/defaultContext.js +0 -14
  106. package/build/es/engine/DataEngine.js +0 -66
  107. package/build/es/engine/DataEngine.test.js +0 -154
  108. package/build/es/engine/helpers/getMutationFetchType.js +0 -1
  109. package/build/es/engine/helpers/getMutationFetchType.test.js +0 -37
  110. package/build/es/engine/helpers/resolveDynamicQuery.js +0 -14
  111. package/build/es/engine/helpers/resolveDynamicQuery.test.js +0 -61
  112. package/build/es/engine/helpers/validate.js +0 -53
  113. package/build/es/engine/helpers/validate.test.js +0 -204
  114. package/build/es/engine/index.js +0 -10
  115. package/build/es/engine/types/DataEngineLink.js +0 -1
  116. package/build/es/engine/types/ExecuteOptions.js +0 -1
  117. package/build/es/engine/types/FetchError.js +0 -17
  118. package/build/es/engine/types/FetchError.test.js +0 -12
  119. package/build/es/engine/types/InvalidQueryError.js +0 -11
  120. package/build/es/engine/types/JsonValue.js +0 -1
  121. package/build/es/engine/types/Mutation.js +0 -1
  122. package/build/es/engine/types/PossiblyDynamic.js +0 -1
  123. package/build/es/engine/types/Query.js +0 -1
  124. package/build/es/engine/types/QueryParameters.js +0 -1
  125. package/build/es/links/CustomDataLink.js +0 -44
  126. package/build/es/links/CustomDataLink.test.js +0 -71
  127. package/build/es/links/ErrorLink.js +0 -13
  128. package/build/es/links/RestAPILink/fetchData.js +0 -71
  129. package/build/es/links/RestAPILink/fetchData.test.js +0 -130
  130. package/build/es/links/RestAPILink/metadataResources.js +0 -16
  131. package/build/es/links/RestAPILink/path.js +0 -7
  132. package/build/es/links/RestAPILink/path.test.js +0 -14
  133. package/build/es/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.js +0 -47
  134. package/build/es/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.test.js +0 -71
  135. package/build/es/links/RestAPILink/queryToRequestOptions/requestContentType.js +0 -70
  136. package/build/es/links/RestAPILink/queryToRequestOptions/requestContentType.test.js +0 -118
  137. package/build/es/links/RestAPILink/queryToRequestOptions/textPlainMatchers.js +0 -151
  138. package/build/es/links/RestAPILink/queryToRequestOptions/textPlainMatchers.test.js +0 -244
  139. package/build/es/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.js +0 -7
  140. package/build/es/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.test.js +0 -18
  141. package/build/es/links/RestAPILink/queryToRequestOptions.js +0 -27
  142. package/build/es/links/RestAPILink/queryToRequestOptions.test.js +0 -105
  143. package/build/es/links/RestAPILink/queryToResourcePath.js +0 -75
  144. package/build/es/links/RestAPILink/queryToResourcePath.test.js +0 -171
  145. package/build/es/links/RestAPILink/validateQuery.js +0 -52
  146. package/build/es/links/RestAPILink/validateQuery.test.js +0 -207
  147. package/build/es/links/RestAPILink.js +0 -26
  148. package/build/es/links/RestAPILink.test.js +0 -19
  149. package/build/es/links/index.js +0 -4
  150. package/build/es/locales/en/translations.json +0 -3
  151. package/build/es/locales/index.js +0 -13
  152. package/build/types/engine/DataEngine.d.ts +0 -13
  153. package/build/types/engine/helpers/getMutationFetchType.d.ts +0 -3
  154. package/build/types/engine/helpers/resolveDynamicQuery.d.ts +0 -2
  155. package/build/types/engine/helpers/validate.d.ts +0 -4
  156. package/build/types/engine/index.d.ts +0 -9
  157. package/build/types/engine/types/DataEngineLink.d.ts +0 -9
  158. package/build/types/engine/types/ExecuteOptions.d.ts +0 -9
  159. package/build/types/engine/types/FetchError.d.ts +0 -19
  160. package/build/types/engine/types/InvalidQueryError.d.ts +0 -5
  161. package/build/types/engine/types/JsonValue.d.ts +0 -6
  162. package/build/types/engine/types/Mutation.d.ts +0 -29
  163. package/build/types/engine/types/PossiblyDynamic.d.ts +0 -1
  164. package/build/types/engine/types/Query.d.ts +0 -24
  165. package/build/types/engine/types/QueryParameters.d.ts +0 -12
  166. package/build/types/links/CustomDataLink.d.ts +0 -17
  167. package/build/types/links/ErrorLink.d.ts +0 -6
  168. package/build/types/links/RestAPILink/fetchData.d.ts +0 -4
  169. package/build/types/links/RestAPILink/metadataResources.d.ts +0 -2
  170. package/build/types/links/RestAPILink/path.d.ts +0 -1
  171. package/build/types/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.d.ts +0 -6
  172. package/build/types/links/RestAPILink/queryToRequestOptions/requestContentType.d.ts +0 -6
  173. package/build/types/links/RestAPILink/queryToRequestOptions/textPlainMatchers.d.ts +0 -14
  174. package/build/types/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.d.ts +0 -2
  175. package/build/types/links/RestAPILink/queryToRequestOptions.d.ts +0 -2
  176. package/build/types/links/RestAPILink/queryToResourcePath.d.ts +0 -3
  177. package/build/types/links/RestAPILink/validateQuery.d.ts +0 -2
  178. package/build/types/links/RestAPILink.d.ts +0 -10
  179. package/build/types/links/index.d.ts +0 -3
  180. package/build/types/react/context/defaultContext.d.ts +0 -4
@@ -1,71 +0,0 @@
1
- import { CustomDataLink } from './CustomDataLink';
2
- describe('CustomDataLink', () => {
3
- it('Should return mocked resource', async () => {
4
- const link = new CustomDataLink({
5
- foo: 'bar'
6
- });
7
- expect(link.executeResourceQuery('read', {
8
- resource: 'foo'
9
- }, {})).resolves.toBe('bar');
10
- });
11
- it('Should throw error on mock miss', async () => {
12
- const link = new CustomDataLink({
13
- foo: 'bar'
14
- });
15
- expect(link.executeResourceQuery('read', {
16
- resource: 'something'
17
- }, {})).rejects.toMatchInlineSnapshot(`[Error: No data provided for resource type something!]`);
18
- });
19
- it('Should swallow miss error with failOnMiss=false', async () => {
20
- const link = new CustomDataLink({
21
- foo: 'bar'
22
- }, {
23
- failOnMiss: false
24
- });
25
- expect(link.executeResourceQuery('read', {
26
- resource: 'something'
27
- }, {})).resolves.toBe(null);
28
- });
29
- it('Should resolve functional resource', async () => {
30
- const link = new CustomDataLink({
31
- foo: async () => 'bar'
32
- });
33
- expect(link.executeResourceQuery('read', {
34
- resource: 'foo'
35
- }, {})).resolves.toBe('bar');
36
- });
37
- it('Should throw if resolves to undefined', async () => {
38
- const link = new CustomDataLink({
39
- foo: async () => undefined
40
- });
41
- expect(link.executeResourceQuery('read', {
42
- resource: 'foo'
43
- }, {})).rejects.toMatchInlineSnapshot(`[Error: The custom function for resource foo must always return a value but returned undefined]`);
44
- });
45
- it('Should swallow functional miss if failOnMiss=false', async () => {
46
- const link = new CustomDataLink({
47
- foo: async () => undefined
48
- }, {
49
- failOnMiss: false
50
- });
51
- expect(link.executeResourceQuery('read', {
52
- resource: 'foo'
53
- }, {})).resolves.toBe(null);
54
- });
55
- it('Should wait forever with loadForever=true', async () => {
56
- jest.useFakeTimers();
57
- const link = new CustomDataLink({}, {
58
- loadForever: true
59
- });
60
- let done = false;
61
- link.executeResourceQuery('read', {
62
- resource: 'foo'
63
- }, {}).then(() => {
64
- done = true;
65
- }).catch(() => {
66
- done = true;
67
- });
68
- jest.advanceTimersByTime(100);
69
- expect(done).toBe(false);
70
- });
71
- });
@@ -1,13 +0,0 @@
1
- function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
2
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
3
- function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
4
- export class ErrorLink {
5
- constructor(errorMessage) {
6
- _defineProperty(this, "errorMessage", void 0);
7
- this.errorMessage = errorMessage;
8
- }
9
- executeResourceQuery() {
10
- console.error(this.errorMessage);
11
- return Promise.reject(this.errorMessage);
12
- }
13
- }
@@ -1,71 +0,0 @@
1
- import { FetchError } from '../../engine';
2
- export const parseContentType = contentType => contentType ? contentType.split(';')[0].trim().toLowerCase() : '';
3
- export const parseStatus = async response => {
4
- const accessError = response.status === 401 || response.status === 403 || response.status === 409;
5
- if (accessError) {
6
- let message;
7
- let details = {};
8
- try {
9
- details = await response.json();
10
- message = details.message;
11
- } catch (e) {
12
- // Do nothing
13
- }
14
-
15
- // Set a message in case of invalid json, or json without 'message' property
16
- if (!message) {
17
- message = response.status === 401 ? 'Unauthorized' : 'Forbidden';
18
- }
19
- throw new FetchError({
20
- type: 'access',
21
- message,
22
- details
23
- });
24
- }
25
- if (response.status < 200 || response.status >= 400) {
26
- const message = `An unknown error occurred - ${response.statusText} (${response.status})`;
27
- let details = {};
28
- try {
29
- details = await response.json();
30
- } catch (e) {
31
- // We can leave details as is if parsing fails
32
- }
33
- throw new FetchError({
34
- type: 'unknown',
35
- message,
36
- details
37
- });
38
- }
39
- return response;
40
- };
41
- export function fetchData(url) {
42
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
43
- return fetch(url, {
44
- ...options,
45
- credentials: 'include',
46
- headers: {
47
- 'X-Requested-With': 'XMLHttpRequest',
48
- Accept: 'application/json',
49
- ...options.headers
50
- }
51
- }).catch(err => {
52
- throw new FetchError({
53
- type: 'network',
54
- message: 'An unknown network error occurred',
55
- details: err
56
- });
57
- }).then(parseStatus).then(async response => {
58
- const contentType = parseContentType(response.headers.get('Content-Type'));
59
-
60
- // 'application/json'
61
- if (contentType === 'application/json') {
62
- return await response.json(); // Will throw if invalid JSON!
63
- }
64
-
65
- // 'text/*'
66
- if (/^text\/[a-z0-9.-]+$/.test(contentType)) {
67
- return await response.text();
68
- }
69
- return await response.blob();
70
- });
71
- }
@@ -1,130 +0,0 @@
1
- import { FetchError } from '../../engine';
2
- import { parseStatus, fetchData, parseContentType } from './fetchData';
3
- describe('networkFetch', () => {
4
- describe('parseContentType', () => {
5
- it('should pass through simple content-types', () => {
6
- expect(parseContentType('text/html')).toBe('text/html');
7
- expect(parseContentType('text/plain')).toBe('text/plain');
8
- expect(parseContentType('application/vnd.api+json')).toBe('application/vnd.api+json');
9
- });
10
- it('should strip parameters', () => {
11
- expect(parseContentType('text/svg+xml;charset=utf-8')).toBe('text/svg+xml');
12
- expect(parseContentType('text/html;testing123')).toBe('text/html');
13
- });
14
- it('should trim type', () => {
15
- expect(parseContentType(' text/xml ')).toBe('text/xml');
16
- expect(parseContentType(' application/json ; charset = utf-8')).toBe('application/json');
17
- });
18
- it('should convert to lower-case', () => {
19
- expect(parseContentType(' Text/XML ')).toBe('text/xml');
20
- expect(parseContentType('application/JSON ; charset = UTF-8')).toBe('application/json');
21
- });
22
- it('should correctly parse application/json with charset param', () => {
23
- expect(parseContentType('application/json;charset=UTF-8')).toBe('application/json');
24
- });
25
- });
26
- describe('parseStatus', () => {
27
- it('should pass through the response for a success status code', async () => {
28
- const response = {
29
- status: 200
30
- };
31
- await expect(parseStatus(response)).resolves.toBe(response);
32
- });
33
- it('should throw an access error for 401, 403 and 409 errors', async () => {
34
- const response401 = {
35
- status: 401,
36
- json: async () => {
37
- throw new Error();
38
- }
39
- };
40
- const response403 = {
41
- status: 403,
42
- json: async () => {
43
- throw new Error();
44
- }
45
- };
46
- const response409 = {
47
- status: 409,
48
- json: async () => ({
49
- message: 'An error occurred'
50
- })
51
- };
52
- expect(parseStatus(response401)).rejects.toMatchObject({
53
- type: 'access',
54
- message: 'Unauthorized',
55
- details: {}
56
- });
57
- expect(parseStatus(response403)).rejects.toMatchObject({
58
- type: 'access',
59
- message: 'Forbidden',
60
- details: {}
61
- });
62
- expect(parseStatus(response409)).rejects.toMatchObject({
63
- type: 'access',
64
- message: 'An error occurred',
65
- details: {
66
- message: 'An error occurred'
67
- }
68
- });
69
- });
70
- it('should throw if an unknown error occurs', () => {
71
- const response = {
72
- status: 500,
73
- statusText: 'Failed',
74
- json: async () => ({
75
- message: 'An error occurred'
76
- })
77
- };
78
- expect(parseStatus(response)).rejects.toMatchObject({
79
- type: 'unknown',
80
- message: `An unknown error occurred - Failed (500)`,
81
- details: {
82
- message: 'An error occurred'
83
- }
84
- });
85
- });
86
- });
87
- describe('fetchData', () => {
88
- const headers = {
89
- 'Content-Type': type => type === 'json' ? 'application/json' : type === 'text' ? 'text/plain' : 'some/other-content-type'
90
- };
91
- const mockFetch = jest.fn(async url => ({
92
- status: 200,
93
- headers: {
94
- get: name => headers[name] && headers[name](url)
95
- },
96
- json: async () => ({
97
- foo: 'bar'
98
- }),
99
- text: async () => 'foobar',
100
- blob: async () => 'blob of foobar'
101
- }));
102
- beforeEach(() => {
103
- jest.clearAllMocks();
104
- });
105
- it('Should correctly parse a successful JSON response', () => {
106
- ;
107
- global.fetch = mockFetch;
108
- expect(fetchData('json', {})).resolves.toMatchObject({
109
- foo: 'bar'
110
- });
111
- });
112
- it('Should correctly parse a successful TEXT response', () => {
113
- ;
114
- global.fetch = mockFetch;
115
- expect(fetchData('text')).resolves.toBe('foobar');
116
- });
117
- it('Should correctly parse a successful BLOB response', () => {
118
- ;
119
- global.fetch = mockFetch;
120
- expect(fetchData('something else')).resolves.toBe('blob of foobar');
121
- });
122
- it('Should throw a FetchError if fetch fails', () => {
123
- ;
124
- global.fetch = jest.fn(async () => {
125
- throw new Error();
126
- });
127
- expect(fetchData('failure', {})).rejects.toBeInstanceOf(FetchError);
128
- });
129
- });
130
- });
@@ -1,16 +0,0 @@
1
- /*
2
- * These are metadata resources (from /api/resources) which are known to support paging.
3
- * They should all also support fields declarations. Only plural resource names are supported.
4
- * This list may be incomplete, and may require updating for new DHIS2 major versions
5
- */
6
-
7
- export const normativeMetadataResources = ['programDataElements', 'indicatorTypes', 'programs', 'optionGroups', 'programRuleVariables', 'reports', 'users', 'constants', 'externalMapLayers', 'analyticsTableHooks', 'pushAnalysis', 'oAuth2Clients', 'validationRules', 'reportTables', 'userGroups', 'sqlViews', 'sections', 'validationNotificationTemplates', 'optionGroupSets', 'organisationUnitGroupSets', 'trackedEntityAttributes', 'dashboardItems', 'categoryCombos', 'programSections', 'trackedEntityTypes', 'dataSetNotificationTemplates', 'maps', 'dataApprovalWorkflows', 'programStages', 'categoryOptionGroups', 'relationshipTypes', 'validationRuleGroups', 'predictors', 'dataSets', 'options', 'organisationUnitLevels', 'dataEntryForms', 'predictorGroups', 'dataElementGroupSets', 'programIndicatorGroups', 'dataApprovalLevels', 'organisationUnits', 'programIndicators', 'dataElements', 'mapViews', 'categories', 'categoryOptionCombos', 'documents', 'indicators', 'optionSets', 'interpretations', 'programRuleActions', 'dataElementGroups', 'attributes', 'validationResults', 'categoryOptions', 'indicatorGroupSets', 'messageConversations', 'dashboards', 'programNotificationTemplates', 'programStageSections', 'legendSets', 'organisationUnitGroups', 'visualizations', 'indicatorGroups', 'programTrackedEntityAttributeGroups', 'programRules', 'categoryOptionGroupSets', 'userRoles', 'eventFilters', 'eventReports', 'eventCharts', 'smsCommands', 'jobConfigurations', 'minMaxDataElements', 'charts', 'dataElementOperands',
8
- // These exist and appear to accept field declarations, but have abnormal behavior when it comes to viewing collections and paging results
9
- 'trackedEntityInstance', 'relationships'];
10
-
11
- // Including non-normative resources listed under /api/resources for future follow-up
12
- export const nonNormativeMetadataResources = ['trackedEntityAttributeValues', 'programInstances', 'expressions', 'programStageInstances', 'externalFileResources', 'icons', 'fileResources', 'metadataVersions', 'dataStores',
13
- // This doesn't exist, but is listed as the plural of 'dataStore' in /api/resources
14
-
15
- // Known but missing from /api/resources
16
- 'userDataStore', 'apps'];
@@ -1,7 +0,0 @@
1
- export const joinPath = function () {
2
- for (var _len = arguments.length, parts = new Array(_len), _key = 0; _key < _len; _key++) {
3
- parts[_key] = arguments[_key];
4
- }
5
- const realParts = parts.filter(part => !!part);
6
- return realParts.map(part => part.replace(/^\/+|\/+$/g, '')).join('/');
7
- };
@@ -1,14 +0,0 @@
1
- import { joinPath } from './path';
2
- describe('Utils', () => {
3
- describe('pathJoin', () => {
4
- it('Should strip all leading and trailing slashes', () => {
5
- expect(joinPath('///test//')).toBe('test');
6
- });
7
- it('Should join path segments with slashes', () => {
8
- expect(joinPath('a', 'b', 'c', 'd')).toBe('a/b/c/d');
9
- });
10
- it('Should only include singular joining slashes', () => {
11
- expect(joinPath('//a/', 'b//', '///c////', '/d')).toBe('a/b/c/d');
12
- });
13
- });
14
- });
@@ -1,47 +0,0 @@
1
- /*
2
- * Requests that expect a "multipart/form-data" Content-Type have been collected by scanning
3
- * the developer documentation:
4
- * https://docs.dhis2.org/master/en/developer/html/dhis2_developer_manual_full.html
5
- */
6
-
7
- // Post to 'dataValues' (send/update a data value; endpoint doesn't support JSON)
8
- // For file-uploads too
9
- export const isDataValue = (type, _ref) => {
10
- let {
11
- resource
12
- } = _ref;
13
- return type === 'create' && (resource === 'dataValues' || resource === 'dataValues/file');
14
- };
15
-
16
- // POST to 'fileResources' (upload a file resource)
17
- export const isFileResourceUpload = (type, _ref2) => {
18
- let {
19
- resource
20
- } = _ref2;
21
- return type === 'create' && resource === 'fileResources';
22
- };
23
-
24
- // POST to 'messageConversations/attachments' (upload a message conversation attachment)
25
- export const isMessageConversationAttachment = (type, _ref3) => {
26
- let {
27
- resource
28
- } = _ref3;
29
- return type === 'create' && resource === 'messageConversations/attachments';
30
- };
31
-
32
- // POST to `staticContent/${key}` (upload staticContent: logo_banner | logo_front)
33
- export const isStaticContentUpload = (type, _ref4) => {
34
- let {
35
- resource
36
- } = _ref4;
37
- const pattern = /^staticContent\/(?:logo_banner|logo_front)$/;
38
- return type === 'create' && pattern.test(resource);
39
- };
40
-
41
- // POST to 'apps' (install an app)
42
- export const isAppInstall = (type, _ref5) => {
43
- let {
44
- resource
45
- } = _ref5;
46
- return type === 'create' && resource === 'apps';
47
- };
@@ -1,71 +0,0 @@
1
- import { isFileResourceUpload, isMessageConversationAttachment, isStaticContentUpload, isAppInstall, isDataValue } from './multipartFormDataMatchers';
2
- describe('isDataValue', () => {
3
- it('returns true for a POST to "dataValues"', () => {
4
- expect(isDataValue('create', {
5
- resource: 'dataValues'
6
- })).toBe(true);
7
- });
8
- it('returns true for a POST to "dataValues/file"', () => {
9
- expect(isDataValue('create', {
10
- resource: 'dataValues/file'
11
- })).toBe(true);
12
- });
13
- it('returns false for a POST to a different resource', () => {
14
- expect(isDataValue('create', {
15
- resource: 'somethingElse'
16
- })).toBe(false);
17
- });
18
- });
19
- describe('isFileResourceUpload', () => {
20
- it('returns true for a POST to "fileResources"', () => {
21
- expect(isFileResourceUpload('create', {
22
- resource: 'fileResources'
23
- })).toBe(true);
24
- });
25
- it('retuns false for a POST to a different resource', () => {
26
- expect(isFileResourceUpload('create', {
27
- resource: 'notFileResources'
28
- })).toBe(false);
29
- });
30
- });
31
- describe('isMessageConversationAttachment', () => {
32
- it('returns true for a POST to "messageConversations/attachments"', () => {
33
- expect(isMessageConversationAttachment('create', {
34
- resource: 'messageConversations/attachments'
35
- })).toBe(true);
36
- });
37
- it('retuns false for a POST to a different resource', () => {
38
- expect(isMessageConversationAttachment('create', {
39
- resource: 'messageConversations/notAttachments'
40
- })).toBe(false);
41
- });
42
- });
43
- describe('isStaticContentUpload', () => {
44
- it('returns true for a POST to "staticContent/logo_banner"', () => {
45
- expect(isStaticContentUpload('create', {
46
- resource: 'staticContent/logo_banner'
47
- })).toBe(true);
48
- });
49
- it('returns true for a POST to "staticContent/logo_front"', () => {
50
- expect(isStaticContentUpload('create', {
51
- resource: 'staticContent/logo_front'
52
- })).toBe(true);
53
- });
54
- it('returns false for a request to a different resource', () => {
55
- expect(isStaticContentUpload('create', {
56
- resource: 'staticContent/no_logo'
57
- })).toBe(false);
58
- });
59
- });
60
- describe('isAppInstall', () => {
61
- it('returns true for a POST to "apps"', () => {
62
- expect(isAppInstall('create', {
63
- resource: 'apps'
64
- })).toBe(true);
65
- });
66
- it('retuns false for a POST to a different resource', () => {
67
- expect(isAppInstall('create', {
68
- resource: 'notApps'
69
- })).toBe(false);
70
- });
71
- });
@@ -1,70 +0,0 @@
1
- import * as multipartFormDataMatchers from './multipartFormDataMatchers';
2
- import * as textPlainMatchers from './textPlainMatchers';
3
- import * as xWwwFormUrlencodedMatchers from './xWwwFormUrlencodedMatchers';
4
- const resourceExpectsTextPlain = (type, query) => Object.values(textPlainMatchers).some(textPlainMatcher => textPlainMatcher(type, query));
5
- const resourceExpectsMultipartFormData = (type, query) => Object.values(multipartFormDataMatchers).some(multipartFormDataMatcher => multipartFormDataMatcher(type, query));
6
- const resourceExpectsXWwwFormUrlencoded = (type, query) => Object.values(xWwwFormUrlencodedMatchers).some(xWwwFormUrlencodedMatcher => xWwwFormUrlencodedMatcher(type, query));
7
- const convertData = (data, initialValue) => {
8
- const dataEntries = Object.entries(data);
9
- if (dataEntries.length === 0) {
10
- throw new Error(`Could not convert data to ${initialValue.constructor.name}: object does not have own enumerable string-keyed properties`);
11
- }
12
- return dataEntries.reduce((convertedData, _ref) => {
13
- let [key, value] = _ref;
14
- convertedData.append(key, value);
15
- return convertedData;
16
- }, initialValue);
17
- };
18
- export const requestContentType = (type, query) => {
19
- if (!query.data) {
20
- return null;
21
- }
22
- if (type === 'json-patch') {
23
- return 'application/json-patch+json';
24
- }
25
- if (resourceExpectsTextPlain(type, query)) {
26
- return 'text/plain';
27
- }
28
- if (resourceExpectsMultipartFormData(type, query)) {
29
- return 'multipart/form-data';
30
- }
31
- if (resourceExpectsXWwwFormUrlencoded(type, query)) {
32
- return 'application/x-www-form-urlencoded';
33
- }
34
- return 'application/json';
35
- };
36
- export const requestHeadersForContentType = contentType => {
37
- /*
38
- * Explicitely setting Content-Type to 'multipart/form-data' produces
39
- * a "multipart boundary not found" error. By not setting a Content-Type
40
- * the browser will correctly set it for us and also apply multipart
41
- * boundaries if the request body is an instance of FormData
42
- * See https://stackoverflow.com/a/39281156/1143502
43
- */
44
- if (!contentType || contentType === 'multipart/form-data') {
45
- return undefined;
46
- }
47
- return {
48
- 'Content-Type': contentType
49
- };
50
- };
51
- export const requestBodyForContentType = (contentType, _ref2) => {
52
- let {
53
- data
54
- } = _ref2;
55
- if (typeof data === 'undefined') {
56
- return undefined;
57
- }
58
- if (contentType === 'application/json' || contentType === 'application/json-patch+json') {
59
- return JSON.stringify(data);
60
- }
61
- if (contentType === 'multipart/form-data') {
62
- return convertData(data, new FormData());
63
- }
64
- if (contentType === 'application/x-www-form-urlencoded') {
65
- return convertData(data, new URLSearchParams());
66
- }
67
-
68
- // 'text/plain'
69
- return data;
70
- };
@@ -1,118 +0,0 @@
1
- import { requestContentType, requestHeadersForContentType, requestBodyForContentType } from './requestContentType';
2
- describe('requestContentType', () => {
3
- it('returns "application/json" for a normal resource', () => {
4
- expect(requestContentType('create', {
5
- resource: 'test',
6
- data: 'test'
7
- })).toBe('application/json');
8
- });
9
- it('returns "application/json-patch+json" when the fetch type is "json-patch"', () => {
10
- expect(requestContentType('json-patch', {
11
- resource: 'test',
12
- data: 'test'
13
- })).toBe('application/json-patch+json');
14
- });
15
- it('returns "multipart/form-data" for a specific resource that expects it', () => {
16
- expect(requestContentType('create', {
17
- resource: 'fileResources',
18
- data: 'test'
19
- })).toBe('multipart/form-data');
20
- });
21
- it('returns "text/plain" for a specific resource that expects it', () => {
22
- expect(requestContentType('create', {
23
- resource: 'messageConversations/feedback',
24
- data: 'test'
25
- })).toBe('text/plain');
26
- });
27
- });
28
- describe('requestHeadersForContentType', () => {
29
- it('returns undefined if contentType is null', () => {
30
- expect(requestHeadersForContentType(null)).toBe(undefined);
31
- });
32
- it('returns undefined if contentType is "multipart/form-data"', () => {
33
- expect(requestHeadersForContentType('multipart/form-data')).toBe(undefined);
34
- });
35
- it('returns a headers object with the contentType for "application/json"', () => {
36
- expect(requestHeadersForContentType('application/json')).toEqual({
37
- 'Content-Type': 'application/json'
38
- });
39
- });
40
- it('returns a headers object with the contentType for "text/plain"', () => {
41
- expect(requestHeadersForContentType('text/plain')).toEqual({
42
- 'Content-Type': 'text/plain'
43
- });
44
- });
45
- });
46
- describe('requestBodyForContentType', () => {
47
- it('returns undefined if data is undefined', () => {
48
- expect(requestBodyForContentType('application/json', {
49
- resource: 'test'
50
- })).toBe(undefined);
51
- });
52
- it('JSON stringifies the data if contentType is "application/json"', () => {
53
- const dataIn = {
54
- a: 'AAAA',
55
- b: 1,
56
- c: true
57
- };
58
- const dataOut = JSON.stringify(dataIn);
59
- expect(requestBodyForContentType('application/json', {
60
- resource: 'test',
61
- data: dataIn
62
- })).toBe(dataOut);
63
- });
64
- it('converts to FormData if contentType is "multipart/form-data"', () => {
65
- const file = new File(['foo'], 'foo.txt', {
66
- type: 'text/plain'
67
- });
68
- const data = {
69
- a: 'AAA',
70
- file
71
- };
72
- const result = requestBodyForContentType('multipart/form-data', {
73
- resource: 'test',
74
- data
75
- });
76
- expect(result instanceof FormData).toBe(true);
77
- expect(result.get('a')).toBe('AAA');
78
- expect(result.get('file')).toBe(file);
79
- });
80
- it('throws an error if contentType is "multipart/form-data" and data does have own string-keyd properties', () => {
81
- expect(() => {
82
- requestBodyForContentType('multipart/form-data', {
83
- resource: 'test',
84
- data: new File(['foo'], 'foo.txt', {
85
- type: 'text/plain'
86
- })
87
- });
88
- }).toThrowErrorMatchingInlineSnapshot(`"Could not convert data to FormData: object does not have own enumerable string-keyed properties"`);
89
- });
90
- it('converts to URLSearchParams if contentType is "application/x-www-form-urlencoded"', () => {
91
- const data = {
92
- a: 'AAA'
93
- };
94
- const result = requestBodyForContentType('application/x-www-form-urlencoded', {
95
- resource: 'test',
96
- data
97
- });
98
- expect(result instanceof URLSearchParams).toBe(true);
99
- expect(result.get('a')).toBe('AAA');
100
- });
101
- it('throws an error if contentType is "application/x-www-form-urlencoded" and data does have own string-keyd properties', () => {
102
- expect(() => {
103
- requestBodyForContentType('application/x-www-form-urlencoded', {
104
- resource: 'test',
105
- data: new File(['foo'], 'foo.txt', {
106
- type: 'text/plain'
107
- })
108
- });
109
- }).toThrowErrorMatchingInlineSnapshot(`"Could not convert data to URLSearchParams: object does not have own enumerable string-keyed properties"`);
110
- });
111
- it('returns the data as received if contentType is "text/plain"', () => {
112
- const data = 'Something';
113
- expect(requestBodyForContentType('text/plain', {
114
- resource: 'messageConversations/feedback',
115
- data
116
- })).toBe(data);
117
- });
118
- });