@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.
- package/build/cjs/__tests__/integration.test.js +6 -15
- package/build/cjs/__tests__/mutations.test.js +4 -11
- package/build/cjs/engine/DataEngine.js +4 -14
- package/build/cjs/engine/DataEngine.test.js +0 -2
- package/build/cjs/engine/helpers/getMutationFetchType.js +0 -2
- package/build/cjs/engine/helpers/getMutationFetchType.test.js +0 -1
- package/build/cjs/engine/helpers/resolveDynamicQuery.js +0 -2
- package/build/cjs/engine/helpers/resolveDynamicQuery.test.js +3 -4
- package/build/cjs/engine/helpers/validate.js +0 -21
- package/build/cjs/engine/helpers/validate.test.js +0 -1
- package/build/cjs/engine/index.js +0 -18
- package/build/cjs/engine/types/FetchError.js +3 -8
- package/build/cjs/engine/types/FetchError.test.js +0 -1
- package/build/cjs/engine/types/InvalidQueryError.js +3 -8
- package/build/cjs/index.js +0 -2
- package/build/cjs/links/CustomDataLink.js +3 -17
- package/build/cjs/links/CustomDataLink.test.js +0 -1
- package/build/cjs/links/ErrorLink.js +3 -7
- package/build/cjs/links/RestAPILink/fetchData.js +12 -22
- package/build/cjs/links/RestAPILink/fetchData.test.js +0 -2
- package/build/cjs/links/RestAPILink/metadataResources.js +9 -7
- package/build/cjs/links/RestAPILink/path.js +0 -3
- package/build/cjs/links/RestAPILink/path.test.js +0 -1
- package/build/cjs/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.js +9 -14
- package/build/cjs/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.test.js +0 -1
- package/build/cjs/links/RestAPILink/queryToRequestOptions/requestContentType.js +4 -31
- package/build/cjs/links/RestAPILink/queryToRequestOptions/requestContentType.test.js +0 -1
- package/build/cjs/links/RestAPILink/queryToRequestOptions/textPlainMatchers.js +24 -40
- package/build/cjs/links/RestAPILink/queryToRequestOptions/textPlainMatchers.test.js +10 -1
- package/build/cjs/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.js +0 -2
- package/build/cjs/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.test.js +0 -1
- package/build/cjs/links/RestAPILink/queryToRequestOptions.js +0 -9
- package/build/cjs/links/RestAPILink/queryToRequestOptions.test.js +0 -1
- package/build/cjs/links/RestAPILink/queryToResourcePath.js +3 -24
- package/build/cjs/links/RestAPILink/queryToResourcePath.test.js +2 -4
- package/build/cjs/links/RestAPILink/validateQuery.js +4 -18
- package/build/cjs/links/RestAPILink/validateQuery.test.js +0 -1
- package/build/cjs/links/RestAPILink.js +3 -14
- package/build/cjs/links/RestAPILink.test.js +0 -2
- package/build/cjs/links/index.js +0 -6
- package/build/cjs/react/components/CustomDataProvider.js +2 -11
- package/build/cjs/react/components/DataMutation.js +1 -4
- package/build/cjs/react/components/DataProvider.js +5 -14
- package/build/cjs/react/components/DataProvider.test.js +2 -9
- package/build/cjs/react/components/DataQuery.js +1 -4
- package/build/cjs/react/context/DataContext.js +2 -8
- package/build/cjs/react/context/defaultContext.js +2 -6
- package/build/cjs/react/context/defaultContext.test.js +0 -1
- package/build/cjs/react/hooks/mergeAndCompareVariables.js +4 -6
- package/build/cjs/react/hooks/mergeAndCompareVariables.test.js +2 -3
- package/build/cjs/react/hooks/stableVariablesHash.js +11 -16
- package/build/cjs/react/hooks/stableVariablesHash.test.js +0 -1
- package/build/cjs/react/hooks/useDataEngine.js +0 -4
- package/build/cjs/react/hooks/useDataMutation.js +0 -7
- package/build/cjs/react/hooks/useDataMutation.test.js +42 -71
- package/build/cjs/react/hooks/useDataQuery.js +11 -22
- package/build/cjs/react/hooks/useDataQuery.test.js +318 -374
- package/build/cjs/react/hooks/useQueryExecutor.js +6 -14
- package/build/cjs/react/hooks/useQueryExecutor.test.js +42 -45
- package/build/cjs/react/hooks/useStaticInput.js +0 -3
- package/build/cjs/react/hooks/useStaticInput.test.js +8 -10
- package/build/cjs/react/index.js +0 -11
- package/build/cjs/setupRTL.js +1 -2
- package/build/es/__tests__/integration.test.js +4 -8
- package/build/es/__tests__/mutations.test.js +2 -4
- package/build/es/engine/DataEngine.js +3 -8
- package/build/es/engine/DataEngine.test.js +0 -1
- package/build/es/engine/helpers/resolveDynamicQuery.test.js +3 -3
- package/build/es/engine/helpers/validate.js +0 -13
- package/build/es/engine/types/FetchError.js +3 -6
- package/build/es/engine/types/InvalidQueryError.js +3 -6
- package/build/es/links/CustomDataLink.js +3 -15
- package/build/es/links/ErrorLink.js +3 -5
- package/build/es/links/RestAPILink/fetchData.js +12 -16
- package/build/es/links/RestAPILink/metadataResources.js +8 -3
- package/build/es/links/RestAPILink/path.js +0 -1
- package/build/es/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.js +9 -4
- package/build/es/links/RestAPILink/queryToRequestOptions/requestContentType.js +2 -19
- package/build/es/links/RestAPILink/queryToRequestOptions/textPlainMatchers.js +24 -20
- package/build/es/links/RestAPILink/queryToRequestOptions/textPlainMatchers.test.js +10 -0
- package/build/es/links/RestAPILink/queryToRequestOptions.js +0 -7
- package/build/es/links/RestAPILink/queryToResourcePath.js +3 -21
- package/build/es/links/RestAPILink/queryToResourcePath.test.js +2 -3
- package/build/es/links/RestAPILink/validateQuery.js +4 -16
- package/build/es/links/RestAPILink.js +3 -8
- package/build/es/react/components/CustomDataProvider.js +1 -1
- package/build/es/react/components/DataMutation.js +1 -1
- package/build/es/react/components/DataProvider.js +3 -1
- package/build/es/react/components/DataProvider.test.js +1 -1
- package/build/es/react/components/DataQuery.js +1 -1
- package/build/es/react/hooks/mergeAndCompareVariables.js +4 -3
- package/build/es/react/hooks/mergeAndCompareVariables.test.js +2 -1
- package/build/es/react/hooks/stableVariablesHash.js +11 -14
- package/build/es/react/hooks/useDataMutation.test.js +17 -39
- package/build/es/react/hooks/useDataQuery.js +11 -15
- package/build/es/react/hooks/useDataQuery.test.js +268 -318
- package/build/es/react/hooks/useQueryExecutor.js +6 -9
- package/build/es/react/hooks/useQueryExecutor.test.js +33 -34
- package/build/es/react/hooks/useStaticInput.test.js +6 -6
- package/build/es/setupRTL.js +1 -1
- package/build/types/engine/types/ExecuteOptions.d.ts +1 -1
- package/build/types/engine/types/FetchError.d.ts +2 -2
- package/build/types/engine/types/JsonValue.d.ts +1 -1
- package/build/types/engine/types/Mutation.d.ts +2 -2
- package/build/types/engine/types/PossiblyDynamic.d.ts +1 -1
- package/build/types/engine/types/Query.d.ts +3 -3
- package/build/types/engine/types/QueryParameters.d.ts +4 -4
- package/build/types/links/CustomDataLink.d.ts +2 -2
- package/build/types/links/RestAPILink/queryToRequestOptions/requestContentType.d.ts +1 -1
- package/build/types/links/RestAPILink/queryToRequestOptions.d.ts +1 -1
- package/build/types/react/hooks/mergeAndCompareVariables.d.ts +1 -1
- package/build/types/types.d.ts +6 -6
- package/package.json +4 -4
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
function _defineProperty(
|
|
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) {
|
|
14
|
-
|
|
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) {
|
|
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, {
|
|
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'));
|
|
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
|
-
}
|
|
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
|
|
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
|
-
};
|
|
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
|
-
};
|
|
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
|
-
};
|
|
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
|
-
};
|
|
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
|
-
}
|
|
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
|
-
};
|
|
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
|
-
};
|
|
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
|
-
};
|
|
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
|
-
};
|
|
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
|
-
};
|
|
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
|
-
};
|
|
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
|
-
};
|
|
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
|
-
};
|
|
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
|
-
};
|
|
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
|
-
|
|
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(
|
|
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
|
-
}
|
|
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 = {
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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(
|
|
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
|
}
|