@dhis2/app-service-data 3.11.2 → 3.12.0-alpha.1

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 (113) hide show
  1. package/build/cjs/__tests__/integration.test.js +6 -15
  2. package/build/cjs/__tests__/mutations.test.js +4 -11
  3. package/build/cjs/engine/DataEngine.js +4 -14
  4. package/build/cjs/engine/DataEngine.test.js +0 -2
  5. package/build/cjs/engine/helpers/getMutationFetchType.js +0 -2
  6. package/build/cjs/engine/helpers/getMutationFetchType.test.js +0 -1
  7. package/build/cjs/engine/helpers/resolveDynamicQuery.js +0 -2
  8. package/build/cjs/engine/helpers/resolveDynamicQuery.test.js +3 -4
  9. package/build/cjs/engine/helpers/validate.js +0 -21
  10. package/build/cjs/engine/helpers/validate.test.js +0 -1
  11. package/build/cjs/engine/index.js +0 -18
  12. package/build/cjs/engine/types/FetchError.js +3 -8
  13. package/build/cjs/engine/types/FetchError.test.js +0 -1
  14. package/build/cjs/engine/types/InvalidQueryError.js +3 -8
  15. package/build/cjs/index.js +0 -2
  16. package/build/cjs/links/CustomDataLink.js +3 -17
  17. package/build/cjs/links/CustomDataLink.test.js +0 -1
  18. package/build/cjs/links/ErrorLink.js +3 -7
  19. package/build/cjs/links/RestAPILink/fetchData.js +12 -22
  20. package/build/cjs/links/RestAPILink/fetchData.test.js +0 -2
  21. package/build/cjs/links/RestAPILink/metadataResources.js +9 -7
  22. package/build/cjs/links/RestAPILink/path.js +0 -3
  23. package/build/cjs/links/RestAPILink/path.test.js +0 -1
  24. package/build/cjs/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.js +9 -14
  25. package/build/cjs/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.test.js +0 -1
  26. package/build/cjs/links/RestAPILink/queryToRequestOptions/requestContentType.js +4 -31
  27. package/build/cjs/links/RestAPILink/queryToRequestOptions/requestContentType.test.js +0 -1
  28. package/build/cjs/links/RestAPILink/queryToRequestOptions/textPlainMatchers.js +24 -40
  29. package/build/cjs/links/RestAPILink/queryToRequestOptions/textPlainMatchers.test.js +10 -1
  30. package/build/cjs/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.js +0 -2
  31. package/build/cjs/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.test.js +0 -1
  32. package/build/cjs/links/RestAPILink/queryToRequestOptions.js +0 -9
  33. package/build/cjs/links/RestAPILink/queryToRequestOptions.test.js +0 -1
  34. package/build/cjs/links/RestAPILink/queryToResourcePath.js +3 -24
  35. package/build/cjs/links/RestAPILink/queryToResourcePath.test.js +2 -4
  36. package/build/cjs/links/RestAPILink/validateQuery.js +4 -18
  37. package/build/cjs/links/RestAPILink/validateQuery.test.js +0 -1
  38. package/build/cjs/links/RestAPILink.js +3 -14
  39. package/build/cjs/links/RestAPILink.test.js +0 -2
  40. package/build/cjs/links/index.js +0 -6
  41. package/build/cjs/react/components/CustomDataProvider.js +2 -11
  42. package/build/cjs/react/components/DataMutation.js +1 -4
  43. package/build/cjs/react/components/DataProvider.js +5 -14
  44. package/build/cjs/react/components/DataProvider.test.js +2 -9
  45. package/build/cjs/react/components/DataQuery.js +1 -4
  46. package/build/cjs/react/context/DataContext.js +2 -8
  47. package/build/cjs/react/context/defaultContext.js +2 -6
  48. package/build/cjs/react/context/defaultContext.test.js +0 -1
  49. package/build/cjs/react/hooks/mergeAndCompareVariables.js +4 -6
  50. package/build/cjs/react/hooks/mergeAndCompareVariables.test.js +2 -3
  51. package/build/cjs/react/hooks/stableVariablesHash.js +11 -16
  52. package/build/cjs/react/hooks/stableVariablesHash.test.js +0 -1
  53. package/build/cjs/react/hooks/useDataEngine.js +0 -4
  54. package/build/cjs/react/hooks/useDataMutation.js +0 -7
  55. package/build/cjs/react/hooks/useDataMutation.test.js +42 -71
  56. package/build/cjs/react/hooks/useDataQuery.js +11 -22
  57. package/build/cjs/react/hooks/useDataQuery.test.js +318 -374
  58. package/build/cjs/react/hooks/useQueryExecutor.js +6 -14
  59. package/build/cjs/react/hooks/useQueryExecutor.test.js +42 -45
  60. package/build/cjs/react/hooks/useStaticInput.js +0 -3
  61. package/build/cjs/react/hooks/useStaticInput.test.js +8 -10
  62. package/build/cjs/react/index.js +0 -11
  63. package/build/cjs/setupRTL.js +1 -2
  64. package/build/es/__tests__/integration.test.js +4 -8
  65. package/build/es/__tests__/mutations.test.js +2 -4
  66. package/build/es/engine/DataEngine.js +3 -8
  67. package/build/es/engine/DataEngine.test.js +0 -1
  68. package/build/es/engine/helpers/resolveDynamicQuery.test.js +3 -3
  69. package/build/es/engine/helpers/validate.js +0 -13
  70. package/build/es/engine/types/FetchError.js +3 -6
  71. package/build/es/engine/types/InvalidQueryError.js +3 -6
  72. package/build/es/links/CustomDataLink.js +3 -15
  73. package/build/es/links/ErrorLink.js +3 -5
  74. package/build/es/links/RestAPILink/fetchData.js +12 -16
  75. package/build/es/links/RestAPILink/metadataResources.js +8 -3
  76. package/build/es/links/RestAPILink/path.js +0 -1
  77. package/build/es/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.js +9 -4
  78. package/build/es/links/RestAPILink/queryToRequestOptions/requestContentType.js +2 -19
  79. package/build/es/links/RestAPILink/queryToRequestOptions/textPlainMatchers.js +24 -20
  80. package/build/es/links/RestAPILink/queryToRequestOptions/textPlainMatchers.test.js +10 -0
  81. package/build/es/links/RestAPILink/queryToRequestOptions.js +0 -7
  82. package/build/es/links/RestAPILink/queryToResourcePath.js +3 -21
  83. package/build/es/links/RestAPILink/queryToResourcePath.test.js +2 -3
  84. package/build/es/links/RestAPILink/validateQuery.js +4 -16
  85. package/build/es/links/RestAPILink.js +3 -8
  86. package/build/es/react/components/CustomDataProvider.js +1 -1
  87. package/build/es/react/components/DataMutation.js +1 -1
  88. package/build/es/react/components/DataProvider.js +3 -1
  89. package/build/es/react/components/DataProvider.test.js +1 -1
  90. package/build/es/react/components/DataQuery.js +1 -1
  91. package/build/es/react/hooks/mergeAndCompareVariables.js +4 -3
  92. package/build/es/react/hooks/mergeAndCompareVariables.test.js +2 -1
  93. package/build/es/react/hooks/stableVariablesHash.js +11 -14
  94. package/build/es/react/hooks/useDataMutation.test.js +17 -39
  95. package/build/es/react/hooks/useDataQuery.js +11 -15
  96. package/build/es/react/hooks/useDataQuery.test.js +268 -318
  97. package/build/es/react/hooks/useQueryExecutor.js +6 -9
  98. package/build/es/react/hooks/useQueryExecutor.test.js +33 -34
  99. package/build/es/react/hooks/useStaticInput.test.js +6 -6
  100. package/build/es/setupRTL.js +1 -1
  101. package/build/types/engine/types/ExecuteOptions.d.ts +1 -1
  102. package/build/types/engine/types/FetchError.d.ts +2 -2
  103. package/build/types/engine/types/JsonValue.d.ts +1 -1
  104. package/build/types/engine/types/Mutation.d.ts +2 -2
  105. package/build/types/engine/types/PossiblyDynamic.d.ts +1 -1
  106. package/build/types/engine/types/Query.d.ts +3 -3
  107. package/build/types/engine/types/QueryParameters.d.ts +4 -4
  108. package/build/types/links/CustomDataLink.d.ts +2 -2
  109. package/build/types/links/RestAPILink/queryToRequestOptions/requestContentType.d.ts +1 -1
  110. package/build/types/links/RestAPILink/queryToRequestOptions.d.ts +1 -1
  111. package/build/types/react/hooks/mergeAndCompareVariables.d.ts +1 -1
  112. package/build/types/types.d.ts +6 -6
  113. package/package.json +4 -4
@@ -1,15 +1,13 @@
1
- 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; }
2
-
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); }
3
4
  export class ErrorLink {
4
5
  constructor(errorMessage) {
5
6
  _defineProperty(this, "errorMessage", void 0);
6
-
7
7
  this.errorMessage = errorMessage;
8
8
  }
9
-
10
9
  executeResourceQuery() {
11
10
  console.error(this.errorMessage);
12
11
  return Promise.reject(this.errorMessage);
13
12
  }
14
-
15
13
  }
@@ -2,50 +2,46 @@ import { FetchError } from '../../engine';
2
2
  export const parseContentType = contentType => contentType ? contentType.split(';')[0].trim().toLowerCase() : '';
3
3
  export const parseStatus = async response => {
4
4
  const accessError = response.status === 401 || response.status === 403 || response.status === 409;
5
-
6
5
  if (accessError) {
7
6
  let message;
8
7
  let details = {};
9
-
10
8
  try {
11
9
  details = await response.json();
12
10
  message = details.message;
13
- } catch (e) {// Do nothing
14
- } // Set a message in case of invalid json, or json without 'message' property
15
-
11
+ } catch (e) {
12
+ // Do nothing
13
+ }
16
14
 
15
+ // Set a message in case of invalid json, or json without 'message' property
17
16
  if (!message) {
18
17
  message = response.status === 401 ? 'Unauthorized' : 'Forbidden';
19
18
  }
20
-
21
19
  throw new FetchError({
22
20
  type: 'access',
23
21
  message,
24
22
  details
25
23
  });
26
24
  }
27
-
28
25
  if (response.status < 200 || response.status >= 400) {
29
26
  const message = `An unknown error occurred - ${response.statusText} (${response.status})`;
30
27
  let details = {};
31
-
32
28
  try {
33
29
  details = await response.json();
34
- } catch (e) {// We can leave details as is if parsing fails
30
+ } catch (e) {
31
+ // We can leave details as is if parsing fails
35
32
  }
36
-
37
33
  throw new FetchError({
38
34
  type: 'unknown',
39
35
  message,
40
36
  details
41
37
  });
42
38
  }
43
-
44
39
  return response;
45
40
  };
46
41
  export function fetchData(url) {
47
42
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
48
- return fetch(url, { ...options,
43
+ return fetch(url, {
44
+ ...options,
49
45
  credentials: 'include',
50
46
  headers: {
51
47
  'X-Requested-With': 'XMLHttpRequest',
@@ -59,17 +55,17 @@ export function fetchData(url) {
59
55
  details: err
60
56
  });
61
57
  }).then(parseStatus).then(async response => {
62
- const contentType = parseContentType(response.headers.get('Content-Type')); // 'application/json'
58
+ const contentType = parseContentType(response.headers.get('Content-Type'));
63
59
 
60
+ // 'application/json'
64
61
  if (contentType === 'application/json') {
65
62
  return await response.json(); // Will throw if invalid JSON!
66
- } // 'text/*'
67
-
63
+ }
68
64
 
65
+ // 'text/*'
69
66
  if (/^text\/[a-z0-9.-]+$/.test(contentType)) {
70
67
  return await response.text();
71
68
  }
72
-
73
69
  return await response.blob();
74
70
  });
75
71
  }
@@ -3,9 +3,14 @@
3
3
  * They should all also support fields declarations. Only plural resource names are supported.
4
4
  * This list may be incomplete, and may require updating for new DHIS2 major versions
5
5
  */
6
- 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', // These exist and appear to accept field declarations, but have abnormal behavior when it comes to viewing collections and paging results
7
- 'trackedEntityInstance', 'relationships']; // Including non-normative resources listed under /api/resources for future follow-up
8
6
 
9
- export const nonNormativeMetadataResources = ['trackedEntityAttributeValues', 'programInstances', 'expressions', 'programStageInstances', 'externalFileResources', 'icons', 'fileResources', 'metadataVersions', 'dataStores', // This doesn't exist, but is listed as the plural of 'dataStore' in /api/resources
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
+
10
15
  // Known but missing from /api/resources
11
16
  'userDataStore', 'apps'];
@@ -2,7 +2,6 @@ export const joinPath = function () {
2
2
  for (var _len = arguments.length, parts = new Array(_len), _key = 0; _key < _len; _key++) {
3
3
  parts[_key] = arguments[_key];
4
4
  }
5
-
6
5
  const realParts = parts.filter(part => !!part);
7
6
  return realParts.map(part => part.replace(/^\/+|\/+$/g, '')).join('/');
8
7
  };
@@ -3,6 +3,7 @@
3
3
  * the developer documentation:
4
4
  * https://docs.dhis2.org/master/en/developer/html/dhis2_developer_manual_full.html
5
5
  */
6
+
6
7
  // Post to 'dataValues' (send/update a data value; endpoint doesn't support JSON)
7
8
  // For file-uploads too
8
9
  export const isDataValue = (type, _ref) => {
@@ -10,30 +11,34 @@ export const isDataValue = (type, _ref) => {
10
11
  resource
11
12
  } = _ref;
12
13
  return type === 'create' && (resource === 'dataValues' || resource === 'dataValues/file');
13
- }; // POST to 'fileResources' (upload a file resource)
14
+ };
14
15
 
16
+ // POST to 'fileResources' (upload a file resource)
15
17
  export const isFileResourceUpload = (type, _ref2) => {
16
18
  let {
17
19
  resource
18
20
  } = _ref2;
19
21
  return type === 'create' && resource === 'fileResources';
20
- }; // POST to 'messageConversations/attachments' (upload a message conversation attachment)
22
+ };
21
23
 
24
+ // POST to 'messageConversations/attachments' (upload a message conversation attachment)
22
25
  export const isMessageConversationAttachment = (type, _ref3) => {
23
26
  let {
24
27
  resource
25
28
  } = _ref3;
26
29
  return type === 'create' && resource === 'messageConversations/attachments';
27
- }; // POST to `staticContent/${key}` (upload staticContent: logo_banner | logo_front)
30
+ };
28
31
 
32
+ // POST to `staticContent/${key}` (upload staticContent: logo_banner | logo_front)
29
33
  export const isStaticContentUpload = (type, _ref4) => {
30
34
  let {
31
35
  resource
32
36
  } = _ref4;
33
37
  const pattern = /^staticContent\/(?:logo_banner|logo_front)$/;
34
38
  return type === 'create' && pattern.test(resource);
35
- }; // POST to 'apps' (install an app)
39
+ };
36
40
 
41
+ // POST to 'apps' (install an app)
37
42
  export const isAppInstall = (type, _ref5) => {
38
43
  let {
39
44
  resource
@@ -1,48 +1,36 @@
1
1
  import * as multipartFormDataMatchers from './multipartFormDataMatchers';
2
2
  import * as textPlainMatchers from './textPlainMatchers';
3
3
  import * as xWwwFormUrlencodedMatchers from './xWwwFormUrlencodedMatchers';
4
-
5
4
  const resourceExpectsTextPlain = (type, query) => Object.values(textPlainMatchers).some(textPlainMatcher => textPlainMatcher(type, query));
6
-
7
5
  const resourceExpectsMultipartFormData = (type, query) => Object.values(multipartFormDataMatchers).some(multipartFormDataMatcher => multipartFormDataMatcher(type, query));
8
-
9
6
  const resourceExpectsXWwwFormUrlencoded = (type, query) => Object.values(xWwwFormUrlencodedMatchers).some(xWwwFormUrlencodedMatcher => xWwwFormUrlencodedMatcher(type, query));
10
-
11
7
  const convertData = (data, initialValue) => {
12
8
  const dataEntries = Object.entries(data);
13
-
14
9
  if (dataEntries.length === 0) {
15
10
  throw new Error(`Could not convert data to ${initialValue.constructor.name}: object does not have own enumerable string-keyed properties`);
16
11
  }
17
-
18
12
  return dataEntries.reduce((convertedData, _ref) => {
19
13
  let [key, value] = _ref;
20
14
  convertedData.append(key, value);
21
15
  return convertedData;
22
16
  }, initialValue);
23
17
  };
24
-
25
18
  export const requestContentType = (type, query) => {
26
19
  if (!query.data) {
27
20
  return null;
28
21
  }
29
-
30
22
  if (type === 'json-patch') {
31
23
  return 'application/json-patch+json';
32
24
  }
33
-
34
25
  if (resourceExpectsTextPlain(type, query)) {
35
26
  return 'text/plain';
36
27
  }
37
-
38
28
  if (resourceExpectsMultipartFormData(type, query)) {
39
29
  return 'multipart/form-data';
40
30
  }
41
-
42
31
  if (resourceExpectsXWwwFormUrlencoded(type, query)) {
43
32
  return 'application/x-www-form-urlencoded';
44
33
  }
45
-
46
34
  return 'application/json';
47
35
  };
48
36
  export const requestHeadersForContentType = contentType => {
@@ -56,7 +44,6 @@ export const requestHeadersForContentType = contentType => {
56
44
  if (!contentType || contentType === 'multipart/form-data') {
57
45
  return undefined;
58
46
  }
59
-
60
47
  return {
61
48
  'Content-Type': contentType
62
49
  };
@@ -65,23 +52,19 @@ export const requestBodyForContentType = (contentType, _ref2) => {
65
52
  let {
66
53
  data
67
54
  } = _ref2;
68
-
69
55
  if (typeof data === 'undefined') {
70
56
  return undefined;
71
57
  }
72
-
73
58
  if (contentType === 'application/json' || contentType === 'application/json-patch+json') {
74
59
  return JSON.stringify(data);
75
60
  }
76
-
77
61
  if (contentType === 'multipart/form-data') {
78
62
  return convertData(data, new FormData());
79
63
  }
80
-
81
64
  if (contentType === 'application/x-www-form-urlencoded') {
82
65
  return convertData(data, new URLSearchParams());
83
- } // 'text/plain'
84
-
66
+ }
85
67
 
68
+ // 'text/plain'
86
69
  return data;
87
70
  };
@@ -8,6 +8,7 @@
8
8
  * the resource property (string). If we decide to allow the `id` property for
9
9
  * "create" mutation-objects, we will have to include additional checks below.
10
10
  */
11
+
11
12
  // POST to `messageConversations/${id}` (reply to a messagConversation)
12
13
  export const isReplyToMessageConversation = (type, _ref) => {
13
14
  let {
@@ -15,76 +16,75 @@ export const isReplyToMessageConversation = (type, _ref) => {
15
16
  } = _ref;
16
17
  const pattern = /^messageConversations\/[a-zA-Z0-9]{11}$/;
17
18
  return type === 'create' && pattern.test(resource);
18
- }; // POST to 'messageConversations/feedback' (create a feedback message)
19
+ };
19
20
 
21
+ // POST to 'messageConversations/feedback' (create a feedback message)
20
22
  export const isCreateFeedbackMessage = (type, _ref2) => {
21
23
  let {
22
24
  resource
23
25
  } = _ref2;
24
26
  return type === 'create' && resource === 'messageConversations/feedback';
25
- }; // POST `interpretations/${objectType}/${id}` (add an interpretation to a visualization)
27
+ };
26
28
 
29
+ // POST `interpretations/${objectType}/${id}` (add an interpretation to a visualization)
27
30
  export const isCreateInterpretation = (type, _ref3) => {
28
31
  let {
29
32
  resource
30
33
  } = _ref3;
31
34
  const pattern = /^interpretations\/(?:reportTable|chart|visualization|map|eventVisualization|eventReport|eventChart|dataSetReport)\/[a-zA-Z0-9]{11}$/;
32
35
  return type === 'create' && pattern.test(resource);
33
- }; // PUT to `interpretations/${id}` (update an interpretation)
36
+ };
34
37
 
38
+ // PUT to `interpretations/${id}` (update an interpretation)
35
39
  export const isUpdateInterpretation = (type, _ref4) => {
36
40
  let {
37
41
  resource,
38
42
  id
39
43
  } = _ref4;
40
-
41
44
  if (type !== 'replace') {
42
45
  return false;
43
46
  }
44
-
45
47
  let resourcePattern;
46
-
47
48
  if (id) {
48
49
  resourcePattern = /^interpretations$/;
49
50
  const idPattern = /^[a-zA-Z0-9]{11}$/;
50
51
  return resourcePattern.test(resource) && idPattern.test(id);
51
52
  }
52
-
53
53
  resourcePattern = /^interpretations\/[a-zA-Z0-9]{11}$/;
54
54
  return resourcePattern.test(resource);
55
- }; // POST to `interpretations/${id}/comments` (comment on an interpretation)
55
+ };
56
56
 
57
+ // POST to `interpretations/${id}/comments` (comment on an interpretation)
57
58
  export const isCommentOnInterpretation = (type, _ref5) => {
58
59
  let {
59
60
  resource
60
61
  } = _ref5;
61
62
  const pattern = /^interpretations\/[a-zA-Z0-9]{11}\/comments$/;
62
63
  return type === 'create' && pattern.test(resource);
63
- }; // PUT to `interpretations/${interpretationId}/comments/${commentId}`
64
- // (update an interpretation comment)
64
+ };
65
65
 
66
+ // PUT to `interpretations/${interpretationId}/comments/${commentId}`
67
+ // (update an interpretation comment)
66
68
  export const isInterpretationCommentUpdate = (type, _ref6) => {
67
69
  let {
68
70
  resource,
69
71
  id
70
72
  } = _ref6;
71
-
72
73
  if (type !== 'replace') {
73
74
  return false;
74
75
  }
75
-
76
76
  if (id) {
77
77
  const idPatternLong = /^[a-zA-Z0-9]{11}\/comments\/[a-zA-Z0-9]{11}$/;
78
78
  const idPatternShort = /^[a-zA-Z0-9]{11}$/;
79
79
  const resourcePattern = /^interpretations\/[a-zA-Z0-9]{11}\/comments$/;
80
80
  return resource === 'interpretations' && idPatternLong.test(id) || resourcePattern.test(resource) && idPatternShort.test(id);
81
81
  }
82
-
83
82
  const pattern = /^interpretations\/[a-zA-Z0-9]{11}\/comments\/[a-zA-Z0-9]{11}$/;
84
83
  return pattern.test(resource);
85
- }; // POST to `systemSettings/${settingKey}` or `userSettings/${settingKey}`
86
- // (add or update a single system or user setting)
84
+ };
87
85
 
86
+ // POST to `systemSettings/${settingKey}` or `userSettings/${settingKey}`
87
+ // (add or update a single system or user setting)
88
88
  export const isAddOrUpdateSystemOrUserSetting = (type, _ref7) => {
89
89
  let {
90
90
  resource
@@ -92,9 +92,10 @@ export const isAddOrUpdateSystemOrUserSetting = (type, _ref7) => {
92
92
  // At least 4 chars because the all start with 'key' (i.e. keyStyle)
93
93
  const pattern = /^(?:systemSettings|userSettings)\/[a-zA-Z]{4,}$/;
94
94
  return type === 'create' && pattern.test(resource);
95
- }; // POST to `configuration/${configurationProperty}`
96
- // (add or update a single configuration property)
95
+ };
97
96
 
97
+ // POST to `configuration/${configurationProperty}`
98
+ // (add or update a single configuration property)
98
99
  export const addOrUpdateConfigurationProperty = (type, _ref8) => {
99
100
  let {
100
101
  resource
@@ -103,18 +104,21 @@ export const addOrUpdateConfigurationProperty = (type, _ref8) => {
103
104
  const pattern = /^(configuration)\/([a-zA-Z]{1,50})$/;
104
105
  const match = resource.match(pattern);
105
106
  return type === 'create' && !!match && match[2] !== 'corsWhitelist';
106
- }; // POST to 'synchronization/metadataPull' (install a metadata package)
107
+ };
107
108
 
109
+ // POST to 'synchronization/metadataPull' (install a metadata package)
108
110
  export const isMetadataPackageInstallation = (type, _ref9) => {
109
111
  let {
110
112
  resource
111
113
  } = _ref9;
112
114
  return type === 'create' && resource === 'synchronization/metadataPull';
113
- }; // POST to 'indicaators/expression/description' (validate an expression)
115
+ };
114
116
 
117
+ // POST to 'indicators/expression/description' or 'programIndicator/expression/description' (validate an expression)
115
118
  export const isExpressionDescriptionValidation = (type, _ref10) => {
116
119
  let {
117
120
  resource
118
121
  } = _ref10;
119
- return type === 'create' && resource === 'indicators/expression/description';
122
+ const pattern = /^(indicators|programIndicators)\/expression\/description$/;
123
+ return type === 'create' && pattern.test(resource);
120
124
  };
@@ -175,4 +175,14 @@ describe('isExpressionDescriptionValidation', () => {
175
175
  resource: 'indicators/expression/somethingelse'
176
176
  })).toBe(false);
177
177
  });
178
+ it('returns true for a POST to "programIndicators/expression/description"', () => {
179
+ expect(isExpressionDescriptionValidation('create', {
180
+ resource: 'programIndicators/expression/description'
181
+ })).toBe(true);
182
+ });
183
+ it('retuns false for a POST to a different resource', () => {
184
+ expect(isMetadataPackageInstallation('create', {
185
+ resource: 'programIndicators/expression/somethingelse'
186
+ })).toBe(false);
187
+ });
178
188
  });
@@ -1,28 +1,21 @@
1
1
  import { requestContentType, requestBodyForContentType, requestHeadersForContentType } from './queryToRequestOptions/requestContentType';
2
-
3
2
  const getMethod = type => {
4
3
  switch (type) {
5
4
  case 'create':
6
5
  return 'POST';
7
-
8
6
  case 'read':
9
7
  return 'GET';
10
-
11
8
  case 'update':
12
9
  case 'json-patch':
13
10
  return 'PATCH';
14
-
15
11
  case 'replace':
16
12
  return 'PUT';
17
-
18
13
  case 'delete':
19
14
  return 'DELETE';
20
-
21
15
  default:
22
16
  throw new Error(`Unknown type ${type}`);
23
17
  }
24
18
  };
25
-
26
19
  export const queryToRequestOptions = (type, query, signal) => {
27
20
  const contentType = requestContentType(type, query);
28
21
  return {
@@ -1,29 +1,22 @@
1
1
  import { joinPath } from './path';
2
2
  import { validateResourceQuery } from './validateQuery';
3
-
4
3
  const encodeQueryParameter = param => {
5
4
  if (Array.isArray(param)) {
6
5
  return param.map(encodeQueryParameter).join(',');
7
6
  }
8
-
9
7
  if (typeof param === 'string') {
10
8
  return encodeURIComponent(param);
11
9
  }
12
-
13
10
  if (typeof param === 'number' || typeof param === 'boolean') {
14
11
  return String(param);
15
12
  }
16
-
17
13
  if (typeof param === 'object') {
18
14
  throw new Error('Object parameter mappings not yet implemented');
19
15
  }
20
-
21
16
  throw new Error('Unknown parameter type');
22
17
  };
23
-
24
18
  const queryParametersMapToArray = params => Object.keys(params).reduce((out, key) => {
25
19
  const value = params[key];
26
-
27
20
  if (key === 'filter' && Array.isArray(value)) {
28
21
  value.forEach(item => {
29
22
  out.push({
@@ -37,13 +30,11 @@ const queryParametersMapToArray = params => Object.keys(params).reduce((out, key
37
30
  value: params[key]
38
31
  });
39
32
  }
40
-
41
33
  return out;
42
34
  }, []);
43
-
44
35
  const queryParametersToQueryString = params => {
45
36
  const expandedParams = queryParametersMapToArray(params);
46
- return expandedParams.map((_ref) => {
37
+ return expandedParams.map(_ref => {
47
38
  let {
48
39
  key,
49
40
  value
@@ -51,30 +42,23 @@ const queryParametersToQueryString = params => {
51
42
  return `${encodeURIComponent(key)}=${encodeQueryParameter(value)}`;
52
43
  }).join('&');
53
44
  };
54
-
55
45
  const actionPrefix = 'action::';
56
-
57
46
  const isAction = resource => resource.startsWith(actionPrefix);
58
-
59
47
  const makeActionPath = resource => joinPath('dhis-web-commons', `${resource.substr(actionPrefix.length)}.action`);
60
-
61
48
  const skipApiVersion = (resource, config) => {
62
49
  if (resource === 'tracker' || resource.startsWith('tracker/')) {
63
50
  var _config$serverVersion, _config$serverVersion2;
64
-
65
51
  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) {
66
52
  return true;
67
53
  }
68
- } // The `/api/ping` endpoint is unversioned
69
-
54
+ }
70
55
 
56
+ // The `/api/ping` endpoint is unversioned
71
57
  if (resource === 'ping') {
72
58
  return true;
73
59
  }
74
-
75
60
  return false;
76
61
  };
77
-
78
62
  export const queryToResourcePath = (link, query, type) => {
79
63
  const {
80
64
  resource,
@@ -84,10 +68,8 @@ export const queryToResourcePath = (link, query, type) => {
84
68
  const apiBase = skipApiVersion(resource, link.config) ? link.unversionedApiPath : link.versionedApiPath;
85
69
  const base = isAction(resource) ? makeActionPath(resource) : joinPath(apiBase, resource, id);
86
70
  validateResourceQuery(query, type);
87
-
88
71
  if (Object.keys(params).length) {
89
72
  return `${base}?${queryParametersToQueryString(params)}`;
90
73
  }
91
-
92
74
  return base;
93
75
  };
@@ -1,8 +1,6 @@
1
1
  import { RestAPILink } from '../RestAPILink';
2
2
  import { queryToResourcePath } from './queryToResourcePath';
3
-
4
3
  const createLink = config => new RestAPILink(config);
5
-
6
4
  const defaultConfig = {
7
5
  basePath: '<base>',
8
6
  apiVersion: '37',
@@ -160,7 +158,8 @@ describe('queryToResourcePath', () => {
160
158
  const query = {
161
159
  resource: 'tracker'
162
160
  };
163
- const v38config = { ...defaultConfig,
161
+ const v38config = {
162
+ ...defaultConfig,
164
163
  serverVersion: {
165
164
  major: 2,
166
165
  minor: 38,
@@ -1,64 +1,52 @@
1
1
  import { normativeMetadataResources } from './metadataResources';
2
-
3
2
  const validatePagination = (query, warn) => {
4
3
  var _query$params, _query$params2;
5
-
6
4
  if (!normativeMetadataResources.includes(query.resource)) {
7
5
  return true;
8
6
  }
9
-
10
7
  if (((_query$params = query.params) === null || _query$params === void 0 ? void 0 : _query$params.paging) === false || ((_query$params2 = query.params) === null || _query$params2 === void 0 ? void 0 : _query$params2.paging) === 'false') {
11
8
  warn('Data queries with paging=false are deprecated and should not be used!', query);
12
9
  return false;
13
- } // TODO: validate sub-resource pagination (i.e. fields=users~paging(1,50)[name] )
10
+ }
14
11
 
12
+ // TODO: validate sub-resource pagination (i.e. fields=users~paging(1,50)[name] )
15
13
 
16
14
  return true;
17
15
  };
18
-
19
16
  const validateDeclarativeFields = (query, warn) => {
20
17
  var _query$params3;
21
-
22
18
  if (!normativeMetadataResources.includes(query.resource)) {
23
19
  return true;
24
20
  }
25
-
26
21
  if (!((_query$params3 = query.params) !== null && _query$params3 !== void 0 && _query$params3.fields)) {
27
22
  warn('Data queries should always specify fields to return', query);
28
23
  return false;
29
24
  } else {
30
25
  var _fields;
31
-
32
26
  let fields = undefined;
33
-
34
27
  if (typeof query.params.fields === 'string') {
35
28
  fields = query.params.fields.split(',').map(field => field.trim());
36
29
  } else if (Array.isArray(query.params.fields)) {
37
30
  fields = query.params.fields.map(field => String(field).trim());
38
31
  }
39
-
40
32
  if ((_fields = fields) !== null && _fields !== void 0 && _fields.find(field => field.match(/(^\*$|^:.+)/))) {
41
33
  warn('Data queries should not use wildcard or dynamic field groups', query.params.fields, query);
42
34
  return false;
43
35
  }
44
- } // TODO: validate sub-resource wildcard fields (i.e. fields=users[*])
45
-
36
+ }
46
37
 
38
+ // TODO: validate sub-resource wildcard fields (i.e. fields=users[*])
47
39
  return true;
48
40
  };
49
-
50
41
  export const validateResourceQuery = (query, type) => {
51
42
  let valid = true;
52
-
53
43
  if (process.env.NODE_ENV === 'development') {
54
44
  // Support build-time dead code elimination in production
55
45
  const warn = console.warn;
56
-
57
46
  if (type === 'read') {
58
47
  valid = validatePagination(query, warn) && valid;
59
48
  valid = validateDeclarativeFields(query, warn) && valid;
60
49
  }
61
50
  }
62
-
63
51
  return valid;
64
52
  };
@@ -1,5 +1,6 @@
1
- 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; }
2
-
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); }
3
4
  import { fetchData } from './RestAPILink/fetchData';
4
5
  import { joinPath } from './RestAPILink/path';
5
6
  import { queryToRequestOptions } from './RestAPILink/queryToRequestOptions';
@@ -7,25 +8,19 @@ import { queryToResourcePath } from './RestAPILink/queryToResourcePath';
7
8
  export class RestAPILink {
8
9
  constructor(config) {
9
10
  _defineProperty(this, "config", void 0);
10
-
11
11
  _defineProperty(this, "versionedApiPath", void 0);
12
-
13
12
  _defineProperty(this, "unversionedApiPath", void 0);
14
-
15
13
  this.config = config;
16
14
  this.versionedApiPath = joinPath('api', String(config.apiVersion));
17
15
  this.unversionedApiPath = joinPath('api');
18
16
  }
19
-
20
17
  fetch(path, options) {
21
18
  return fetchData(joinPath(this.config.baseUrl, path), options);
22
19
  }
23
-
24
20
  executeResourceQuery(type, query, _ref) {
25
21
  let {
26
22
  signal
27
23
  } = _ref;
28
24
  return this.fetch(queryToResourcePath(this, query, type), queryToRequestOptions(type, query, signal));
29
25
  }
30
-
31
26
  }