@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.
- package/build/cjs/__tests__/integration.test.js +10 -16
- package/build/cjs/__tests__/mutations.test.js +5 -8
- package/build/cjs/index.js +16 -44
- package/build/cjs/react/components/CustomDataProvider.js +9 -11
- package/build/cjs/react/components/DataMutation.js +7 -8
- package/build/cjs/react/components/DataProvider.js +3 -4
- package/build/cjs/react/components/DataProvider.test.js +3 -4
- package/build/cjs/react/components/DataQuery.js +8 -9
- package/build/cjs/react/components/index.js +33 -0
- package/build/cjs/react/context/DataContext.js +2 -2
- package/build/cjs/react/context/defaultDataContext.js +13 -0
- package/build/cjs/react/context/{defaultContext.test.js → defaultDataContext.test.js} +3 -3
- package/build/cjs/react/hooks/index.js +26 -0
- package/build/cjs/react/hooks/useDataMutation.js +6 -7
- package/build/cjs/react/hooks/useDataMutation.test.js +44 -71
- package/build/cjs/react/hooks/useDataQuery.js +10 -14
- package/build/cjs/react/hooks/useDataQuery.test.js +172 -265
- package/build/cjs/react/hooks/useQueryExecutor.js +11 -13
- package/build/cjs/react/hooks/useQueryExecutor.test.js +12 -16
- package/build/cjs/react/hooks/useStaticInput.js +4 -5
- package/build/cjs/react/hooks/useStaticInput.test.js +24 -39
- package/build/cjs/react/index.js +22 -77
- package/build/es/__tests__/integration.test.js +10 -16
- package/build/es/__tests__/mutations.test.js +5 -8
- package/build/es/index.js +2 -3
- package/build/es/react/components/CustomDataProvider.js +7 -9
- package/build/es/react/components/DataMutation.js +7 -8
- package/build/es/react/components/DataProvider.js +1 -2
- package/build/es/react/components/DataProvider.test.js +1 -2
- package/build/es/react/components/DataQuery.js +8 -9
- package/build/es/react/components/index.js +4 -0
- package/build/es/react/context/DataContext.js +2 -2
- package/build/es/react/context/{defaultContext.js → defaultDataContext.js} +2 -3
- package/build/es/react/context/{defaultContext.test.js → defaultDataContext.test.js} +3 -3
- package/build/es/react/hooks/index.js +3 -0
- package/build/es/react/hooks/useDataMutation.js +6 -7
- package/build/es/react/hooks/useDataMutation.test.js +44 -71
- package/build/es/react/hooks/useDataQuery.js +10 -14
- package/build/es/react/hooks/useDataQuery.test.js +172 -265
- package/build/es/react/hooks/useQueryExecutor.js +10 -12
- package/build/es/react/hooks/useQueryExecutor.test.js +12 -16
- package/build/es/react/hooks/useStaticInput.js +4 -5
- package/build/es/react/hooks/useStaticInput.test.js +24 -39
- package/build/es/react/index.js +3 -11
- package/build/types/index.d.ts +2 -3
- package/build/types/react/components/CustomDataProvider.d.ts +1 -1
- package/build/types/react/components/DataMutation.d.ts +1 -1
- package/build/types/react/components/DataQuery.d.ts +1 -1
- package/build/types/react/components/index.d.ts +4 -0
- package/build/types/react/context/defaultDataContext.d.ts +4 -0
- package/build/types/react/hooks/index.d.ts +3 -0
- package/build/types/react/hooks/mergeAndCompareVariables.d.ts +1 -1
- package/build/types/react/hooks/useDataEngine.d.ts +1 -1
- package/build/types/react/hooks/useDataMutation.d.ts +1 -1
- package/build/types/react/hooks/useDataQuery.d.ts +1 -1
- package/build/types/react/index.d.ts +2 -11
- package/build/types/types.d.ts +2 -7
- package/package.json +4 -3
- package/build/cjs/engine/DataEngine.js +0 -73
- package/build/cjs/engine/DataEngine.test.js +0 -156
- package/build/cjs/engine/helpers/getMutationFetchType.js +0 -8
- package/build/cjs/engine/helpers/getMutationFetchType.test.js +0 -39
- package/build/cjs/engine/helpers/resolveDynamicQuery.js +0 -21
- package/build/cjs/engine/helpers/resolveDynamicQuery.test.js +0 -63
- package/build/cjs/engine/helpers/validate.js +0 -62
- package/build/cjs/engine/helpers/validate.test.js +0 -206
- package/build/cjs/engine/index.js +0 -104
- package/build/cjs/engine/types/DataEngineLink.js +0 -1
- package/build/cjs/engine/types/ExecuteOptions.js +0 -1
- package/build/cjs/engine/types/FetchError.js +0 -24
- package/build/cjs/engine/types/FetchError.test.js +0 -14
- package/build/cjs/engine/types/InvalidQueryError.js +0 -18
- package/build/cjs/engine/types/JsonValue.js +0 -1
- package/build/cjs/engine/types/Mutation.js +0 -1
- package/build/cjs/engine/types/PossiblyDynamic.js +0 -1
- package/build/cjs/engine/types/Query.js +0 -1
- package/build/cjs/engine/types/QueryParameters.js +0 -1
- package/build/cjs/links/CustomDataLink.js +0 -51
- package/build/cjs/links/CustomDataLink.test.js +0 -73
- package/build/cjs/links/ErrorLink.js +0 -20
- package/build/cjs/links/RestAPILink/fetchData.js +0 -80
- package/build/cjs/links/RestAPILink/fetchData.test.js +0 -132
- package/build/cjs/links/RestAPILink/metadataResources.js +0 -22
- package/build/cjs/links/RestAPILink/path.js +0 -14
- package/build/cjs/links/RestAPILink/path.test.js +0 -16
- package/build/cjs/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.js +0 -58
- package/build/cjs/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.test.js +0 -73
- package/build/cjs/links/RestAPILink/queryToRequestOptions/requestContentType.js +0 -80
- package/build/cjs/links/RestAPILink/queryToRequestOptions/requestContentType.test.js +0 -120
- package/build/cjs/links/RestAPILink/queryToRequestOptions/textPlainMatchers.js +0 -170
- package/build/cjs/links/RestAPILink/queryToRequestOptions/textPlainMatchers.test.js +0 -246
- package/build/cjs/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.js +0 -14
- package/build/cjs/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.test.js +0 -20
- package/build/cjs/links/RestAPILink/queryToRequestOptions.js +0 -34
- package/build/cjs/links/RestAPILink/queryToRequestOptions.test.js +0 -107
- package/build/cjs/links/RestAPILink/queryToResourcePath.js +0 -82
- package/build/cjs/links/RestAPILink/queryToResourcePath.test.js +0 -173
- package/build/cjs/links/RestAPILink/validateQuery.js +0 -59
- package/build/cjs/links/RestAPILink/validateQuery.test.js +0 -209
- package/build/cjs/links/RestAPILink.js +0 -33
- package/build/cjs/links/RestAPILink.test.js +0 -21
- package/build/cjs/links/index.js +0 -38
- package/build/cjs/locales/en/translations.json +0 -3
- package/build/cjs/locales/index.js +0 -21
- package/build/cjs/react/context/defaultContext.js +0 -14
- package/build/es/engine/DataEngine.js +0 -66
- package/build/es/engine/DataEngine.test.js +0 -154
- package/build/es/engine/helpers/getMutationFetchType.js +0 -1
- package/build/es/engine/helpers/getMutationFetchType.test.js +0 -37
- package/build/es/engine/helpers/resolveDynamicQuery.js +0 -14
- package/build/es/engine/helpers/resolveDynamicQuery.test.js +0 -61
- package/build/es/engine/helpers/validate.js +0 -53
- package/build/es/engine/helpers/validate.test.js +0 -204
- package/build/es/engine/index.js +0 -10
- package/build/es/engine/types/DataEngineLink.js +0 -1
- package/build/es/engine/types/ExecuteOptions.js +0 -1
- package/build/es/engine/types/FetchError.js +0 -17
- package/build/es/engine/types/FetchError.test.js +0 -12
- package/build/es/engine/types/InvalidQueryError.js +0 -11
- package/build/es/engine/types/JsonValue.js +0 -1
- package/build/es/engine/types/Mutation.js +0 -1
- package/build/es/engine/types/PossiblyDynamic.js +0 -1
- package/build/es/engine/types/Query.js +0 -1
- package/build/es/engine/types/QueryParameters.js +0 -1
- package/build/es/links/CustomDataLink.js +0 -44
- package/build/es/links/CustomDataLink.test.js +0 -71
- package/build/es/links/ErrorLink.js +0 -13
- package/build/es/links/RestAPILink/fetchData.js +0 -71
- package/build/es/links/RestAPILink/fetchData.test.js +0 -130
- package/build/es/links/RestAPILink/metadataResources.js +0 -16
- package/build/es/links/RestAPILink/path.js +0 -7
- package/build/es/links/RestAPILink/path.test.js +0 -14
- package/build/es/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.js +0 -47
- package/build/es/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.test.js +0 -71
- package/build/es/links/RestAPILink/queryToRequestOptions/requestContentType.js +0 -70
- package/build/es/links/RestAPILink/queryToRequestOptions/requestContentType.test.js +0 -118
- package/build/es/links/RestAPILink/queryToRequestOptions/textPlainMatchers.js +0 -151
- package/build/es/links/RestAPILink/queryToRequestOptions/textPlainMatchers.test.js +0 -244
- package/build/es/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.js +0 -7
- package/build/es/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.test.js +0 -18
- package/build/es/links/RestAPILink/queryToRequestOptions.js +0 -27
- package/build/es/links/RestAPILink/queryToRequestOptions.test.js +0 -105
- package/build/es/links/RestAPILink/queryToResourcePath.js +0 -75
- package/build/es/links/RestAPILink/queryToResourcePath.test.js +0 -171
- package/build/es/links/RestAPILink/validateQuery.js +0 -52
- package/build/es/links/RestAPILink/validateQuery.test.js +0 -207
- package/build/es/links/RestAPILink.js +0 -26
- package/build/es/links/RestAPILink.test.js +0 -19
- package/build/es/links/index.js +0 -4
- package/build/es/locales/en/translations.json +0 -3
- package/build/es/locales/index.js +0 -13
- package/build/types/engine/DataEngine.d.ts +0 -13
- package/build/types/engine/helpers/getMutationFetchType.d.ts +0 -3
- package/build/types/engine/helpers/resolveDynamicQuery.d.ts +0 -2
- package/build/types/engine/helpers/validate.d.ts +0 -4
- package/build/types/engine/index.d.ts +0 -9
- package/build/types/engine/types/DataEngineLink.d.ts +0 -9
- package/build/types/engine/types/ExecuteOptions.d.ts +0 -9
- package/build/types/engine/types/FetchError.d.ts +0 -19
- package/build/types/engine/types/InvalidQueryError.d.ts +0 -5
- package/build/types/engine/types/JsonValue.d.ts +0 -6
- package/build/types/engine/types/Mutation.d.ts +0 -29
- package/build/types/engine/types/PossiblyDynamic.d.ts +0 -1
- package/build/types/engine/types/Query.d.ts +0 -24
- package/build/types/engine/types/QueryParameters.d.ts +0 -12
- package/build/types/links/CustomDataLink.d.ts +0 -17
- package/build/types/links/ErrorLink.d.ts +0 -6
- package/build/types/links/RestAPILink/fetchData.d.ts +0 -4
- package/build/types/links/RestAPILink/metadataResources.d.ts +0 -2
- package/build/types/links/RestAPILink/path.d.ts +0 -1
- package/build/types/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.d.ts +0 -6
- package/build/types/links/RestAPILink/queryToRequestOptions/requestContentType.d.ts +0 -6
- package/build/types/links/RestAPILink/queryToRequestOptions/textPlainMatchers.d.ts +0 -14
- package/build/types/links/RestAPILink/queryToRequestOptions/xWwwFormUrlencodedMatchers.d.ts +0 -2
- package/build/types/links/RestAPILink/queryToRequestOptions.d.ts +0 -2
- package/build/types/links/RestAPILink/queryToResourcePath.d.ts +0 -3
- package/build/types/links/RestAPILink/validateQuery.d.ts +0 -2
- package/build/types/links/RestAPILink.d.ts +0 -10
- package/build/types/links/index.d.ts +0 -3
- package/build/types/react/context/defaultContext.d.ts +0 -4
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.fetchData = fetchData;
|
|
7
|
-
exports.parseStatus = exports.parseContentType = void 0;
|
|
8
|
-
var _engine = require("../../engine");
|
|
9
|
-
const parseContentType = contentType => contentType ? contentType.split(';')[0].trim().toLowerCase() : '';
|
|
10
|
-
exports.parseContentType = parseContentType;
|
|
11
|
-
const parseStatus = async response => {
|
|
12
|
-
const accessError = response.status === 401 || response.status === 403 || response.status === 409;
|
|
13
|
-
if (accessError) {
|
|
14
|
-
let message;
|
|
15
|
-
let details = {};
|
|
16
|
-
try {
|
|
17
|
-
details = await response.json();
|
|
18
|
-
message = details.message;
|
|
19
|
-
} catch (e) {
|
|
20
|
-
// Do nothing
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Set a message in case of invalid json, or json without 'message' property
|
|
24
|
-
if (!message) {
|
|
25
|
-
message = response.status === 401 ? 'Unauthorized' : 'Forbidden';
|
|
26
|
-
}
|
|
27
|
-
throw new _engine.FetchError({
|
|
28
|
-
type: 'access',
|
|
29
|
-
message,
|
|
30
|
-
details
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
if (response.status < 200 || response.status >= 400) {
|
|
34
|
-
const message = `An unknown error occurred - ${response.statusText} (${response.status})`;
|
|
35
|
-
let details = {};
|
|
36
|
-
try {
|
|
37
|
-
details = await response.json();
|
|
38
|
-
} catch (e) {
|
|
39
|
-
// We can leave details as is if parsing fails
|
|
40
|
-
}
|
|
41
|
-
throw new _engine.FetchError({
|
|
42
|
-
type: 'unknown',
|
|
43
|
-
message,
|
|
44
|
-
details
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
return response;
|
|
48
|
-
};
|
|
49
|
-
exports.parseStatus = parseStatus;
|
|
50
|
-
function fetchData(url) {
|
|
51
|
-
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
52
|
-
return fetch(url, {
|
|
53
|
-
...options,
|
|
54
|
-
credentials: 'include',
|
|
55
|
-
headers: {
|
|
56
|
-
'X-Requested-With': 'XMLHttpRequest',
|
|
57
|
-
Accept: 'application/json',
|
|
58
|
-
...options.headers
|
|
59
|
-
}
|
|
60
|
-
}).catch(err => {
|
|
61
|
-
throw new _engine.FetchError({
|
|
62
|
-
type: 'network',
|
|
63
|
-
message: 'An unknown network error occurred',
|
|
64
|
-
details: err
|
|
65
|
-
});
|
|
66
|
-
}).then(parseStatus).then(async response => {
|
|
67
|
-
const contentType = parseContentType(response.headers.get('Content-Type'));
|
|
68
|
-
|
|
69
|
-
// 'application/json'
|
|
70
|
-
if (contentType === 'application/json') {
|
|
71
|
-
return await response.json(); // Will throw if invalid JSON!
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// 'text/*'
|
|
75
|
-
if (/^text\/[a-z0-9.-]+$/.test(contentType)) {
|
|
76
|
-
return await response.text();
|
|
77
|
-
}
|
|
78
|
-
return await response.blob();
|
|
79
|
-
});
|
|
80
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _engine = require("../../engine");
|
|
4
|
-
var _fetchData = require("./fetchData");
|
|
5
|
-
describe('networkFetch', () => {
|
|
6
|
-
describe('parseContentType', () => {
|
|
7
|
-
it('should pass through simple content-types', () => {
|
|
8
|
-
expect((0, _fetchData.parseContentType)('text/html')).toBe('text/html');
|
|
9
|
-
expect((0, _fetchData.parseContentType)('text/plain')).toBe('text/plain');
|
|
10
|
-
expect((0, _fetchData.parseContentType)('application/vnd.api+json')).toBe('application/vnd.api+json');
|
|
11
|
-
});
|
|
12
|
-
it('should strip parameters', () => {
|
|
13
|
-
expect((0, _fetchData.parseContentType)('text/svg+xml;charset=utf-8')).toBe('text/svg+xml');
|
|
14
|
-
expect((0, _fetchData.parseContentType)('text/html;testing123')).toBe('text/html');
|
|
15
|
-
});
|
|
16
|
-
it('should trim type', () => {
|
|
17
|
-
expect((0, _fetchData.parseContentType)(' text/xml ')).toBe('text/xml');
|
|
18
|
-
expect((0, _fetchData.parseContentType)(' application/json ; charset = utf-8')).toBe('application/json');
|
|
19
|
-
});
|
|
20
|
-
it('should convert to lower-case', () => {
|
|
21
|
-
expect((0, _fetchData.parseContentType)(' Text/XML ')).toBe('text/xml');
|
|
22
|
-
expect((0, _fetchData.parseContentType)('application/JSON ; charset = UTF-8')).toBe('application/json');
|
|
23
|
-
});
|
|
24
|
-
it('should correctly parse application/json with charset param', () => {
|
|
25
|
-
expect((0, _fetchData.parseContentType)('application/json;charset=UTF-8')).toBe('application/json');
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
describe('parseStatus', () => {
|
|
29
|
-
it('should pass through the response for a success status code', async () => {
|
|
30
|
-
const response = {
|
|
31
|
-
status: 200
|
|
32
|
-
};
|
|
33
|
-
await expect((0, _fetchData.parseStatus)(response)).resolves.toBe(response);
|
|
34
|
-
});
|
|
35
|
-
it('should throw an access error for 401, 403 and 409 errors', async () => {
|
|
36
|
-
const response401 = {
|
|
37
|
-
status: 401,
|
|
38
|
-
json: async () => {
|
|
39
|
-
throw new Error();
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
const response403 = {
|
|
43
|
-
status: 403,
|
|
44
|
-
json: async () => {
|
|
45
|
-
throw new Error();
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
const response409 = {
|
|
49
|
-
status: 409,
|
|
50
|
-
json: async () => ({
|
|
51
|
-
message: 'An error occurred'
|
|
52
|
-
})
|
|
53
|
-
};
|
|
54
|
-
expect((0, _fetchData.parseStatus)(response401)).rejects.toMatchObject({
|
|
55
|
-
type: 'access',
|
|
56
|
-
message: 'Unauthorized',
|
|
57
|
-
details: {}
|
|
58
|
-
});
|
|
59
|
-
expect((0, _fetchData.parseStatus)(response403)).rejects.toMatchObject({
|
|
60
|
-
type: 'access',
|
|
61
|
-
message: 'Forbidden',
|
|
62
|
-
details: {}
|
|
63
|
-
});
|
|
64
|
-
expect((0, _fetchData.parseStatus)(response409)).rejects.toMatchObject({
|
|
65
|
-
type: 'access',
|
|
66
|
-
message: 'An error occurred',
|
|
67
|
-
details: {
|
|
68
|
-
message: 'An error occurred'
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
it('should throw if an unknown error occurs', () => {
|
|
73
|
-
const response = {
|
|
74
|
-
status: 500,
|
|
75
|
-
statusText: 'Failed',
|
|
76
|
-
json: async () => ({
|
|
77
|
-
message: 'An error occurred'
|
|
78
|
-
})
|
|
79
|
-
};
|
|
80
|
-
expect((0, _fetchData.parseStatus)(response)).rejects.toMatchObject({
|
|
81
|
-
type: 'unknown',
|
|
82
|
-
message: `An unknown error occurred - Failed (500)`,
|
|
83
|
-
details: {
|
|
84
|
-
message: 'An error occurred'
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
describe('fetchData', () => {
|
|
90
|
-
const headers = {
|
|
91
|
-
'Content-Type': type => type === 'json' ? 'application/json' : type === 'text' ? 'text/plain' : 'some/other-content-type'
|
|
92
|
-
};
|
|
93
|
-
const mockFetch = jest.fn(async url => ({
|
|
94
|
-
status: 200,
|
|
95
|
-
headers: {
|
|
96
|
-
get: name => headers[name] && headers[name](url)
|
|
97
|
-
},
|
|
98
|
-
json: async () => ({
|
|
99
|
-
foo: 'bar'
|
|
100
|
-
}),
|
|
101
|
-
text: async () => 'foobar',
|
|
102
|
-
blob: async () => 'blob of foobar'
|
|
103
|
-
}));
|
|
104
|
-
beforeEach(() => {
|
|
105
|
-
jest.clearAllMocks();
|
|
106
|
-
});
|
|
107
|
-
it('Should correctly parse a successful JSON response', () => {
|
|
108
|
-
;
|
|
109
|
-
global.fetch = mockFetch;
|
|
110
|
-
expect((0, _fetchData.fetchData)('json', {})).resolves.toMatchObject({
|
|
111
|
-
foo: 'bar'
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
it('Should correctly parse a successful TEXT response', () => {
|
|
115
|
-
;
|
|
116
|
-
global.fetch = mockFetch;
|
|
117
|
-
expect((0, _fetchData.fetchData)('text')).resolves.toBe('foobar');
|
|
118
|
-
});
|
|
119
|
-
it('Should correctly parse a successful BLOB response', () => {
|
|
120
|
-
;
|
|
121
|
-
global.fetch = mockFetch;
|
|
122
|
-
expect((0, _fetchData.fetchData)('something else')).resolves.toBe('blob of foobar');
|
|
123
|
-
});
|
|
124
|
-
it('Should throw a FetchError if fetch fails', () => {
|
|
125
|
-
;
|
|
126
|
-
global.fetch = jest.fn(async () => {
|
|
127
|
-
throw new Error();
|
|
128
|
-
});
|
|
129
|
-
expect((0, _fetchData.fetchData)('failure', {})).rejects.toBeInstanceOf(_engine.FetchError);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.normativeMetadataResources = exports.nonNormativeMetadataResources = void 0;
|
|
7
|
-
/*
|
|
8
|
-
* These are metadata resources (from /api/resources) which are known to support paging.
|
|
9
|
-
* They should all also support fields declarations. Only plural resource names are supported.
|
|
10
|
-
* This list may be incomplete, and may require updating for new DHIS2 major versions
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const normativeMetadataResources = exports.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',
|
|
14
|
-
// These exist and appear to accept field declarations, but have abnormal behavior when it comes to viewing collections and paging results
|
|
15
|
-
'trackedEntityInstance', 'relationships'];
|
|
16
|
-
|
|
17
|
-
// Including non-normative resources listed under /api/resources for future follow-up
|
|
18
|
-
const nonNormativeMetadataResources = exports.nonNormativeMetadataResources = ['trackedEntityAttributeValues', 'programInstances', 'expressions', 'programStageInstances', 'externalFileResources', 'icons', 'fileResources', 'metadataVersions', 'dataStores',
|
|
19
|
-
// This doesn't exist, but is listed as the plural of 'dataStore' in /api/resources
|
|
20
|
-
|
|
21
|
-
// Known but missing from /api/resources
|
|
22
|
-
'userDataStore', 'apps'];
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.joinPath = void 0;
|
|
7
|
-
const joinPath = function () {
|
|
8
|
-
for (var _len = arguments.length, parts = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
9
|
-
parts[_key] = arguments[_key];
|
|
10
|
-
}
|
|
11
|
-
const realParts = parts.filter(part => !!part);
|
|
12
|
-
return realParts.map(part => part.replace(/^\/+|\/+$/g, '')).join('/');
|
|
13
|
-
};
|
|
14
|
-
exports.joinPath = joinPath;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _path = require("./path");
|
|
4
|
-
describe('Utils', () => {
|
|
5
|
-
describe('pathJoin', () => {
|
|
6
|
-
it('Should strip all leading and trailing slashes', () => {
|
|
7
|
-
expect((0, _path.joinPath)('///test//')).toBe('test');
|
|
8
|
-
});
|
|
9
|
-
it('Should join path segments with slashes', () => {
|
|
10
|
-
expect((0, _path.joinPath)('a', 'b', 'c', 'd')).toBe('a/b/c/d');
|
|
11
|
-
});
|
|
12
|
-
it('Should only include singular joining slashes', () => {
|
|
13
|
-
expect((0, _path.joinPath)('//a/', 'b//', '///c////', '/d')).toBe('a/b/c/d');
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
});
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.isStaticContentUpload = exports.isMessageConversationAttachment = exports.isFileResourceUpload = exports.isDataValue = exports.isAppInstall = void 0;
|
|
7
|
-
/*
|
|
8
|
-
* Requests that expect a "multipart/form-data" Content-Type have been collected by scanning
|
|
9
|
-
* the developer documentation:
|
|
10
|
-
* https://docs.dhis2.org/master/en/developer/html/dhis2_developer_manual_full.html
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
// Post to 'dataValues' (send/update a data value; endpoint doesn't support JSON)
|
|
14
|
-
// For file-uploads too
|
|
15
|
-
const isDataValue = (type, _ref) => {
|
|
16
|
-
let {
|
|
17
|
-
resource
|
|
18
|
-
} = _ref;
|
|
19
|
-
return type === 'create' && (resource === 'dataValues' || resource === 'dataValues/file');
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// POST to 'fileResources' (upload a file resource)
|
|
23
|
-
exports.isDataValue = isDataValue;
|
|
24
|
-
const isFileResourceUpload = (type, _ref2) => {
|
|
25
|
-
let {
|
|
26
|
-
resource
|
|
27
|
-
} = _ref2;
|
|
28
|
-
return type === 'create' && resource === 'fileResources';
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// POST to 'messageConversations/attachments' (upload a message conversation attachment)
|
|
32
|
-
exports.isFileResourceUpload = isFileResourceUpload;
|
|
33
|
-
const isMessageConversationAttachment = (type, _ref3) => {
|
|
34
|
-
let {
|
|
35
|
-
resource
|
|
36
|
-
} = _ref3;
|
|
37
|
-
return type === 'create' && resource === 'messageConversations/attachments';
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// POST to `staticContent/${key}` (upload staticContent: logo_banner | logo_front)
|
|
41
|
-
exports.isMessageConversationAttachment = isMessageConversationAttachment;
|
|
42
|
-
const isStaticContentUpload = (type, _ref4) => {
|
|
43
|
-
let {
|
|
44
|
-
resource
|
|
45
|
-
} = _ref4;
|
|
46
|
-
const pattern = /^staticContent\/(?:logo_banner|logo_front)$/;
|
|
47
|
-
return type === 'create' && pattern.test(resource);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// POST to 'apps' (install an app)
|
|
51
|
-
exports.isStaticContentUpload = isStaticContentUpload;
|
|
52
|
-
const isAppInstall = (type, _ref5) => {
|
|
53
|
-
let {
|
|
54
|
-
resource
|
|
55
|
-
} = _ref5;
|
|
56
|
-
return type === 'create' && resource === 'apps';
|
|
57
|
-
};
|
|
58
|
-
exports.isAppInstall = isAppInstall;
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _multipartFormDataMatchers = require("./multipartFormDataMatchers");
|
|
4
|
-
describe('isDataValue', () => {
|
|
5
|
-
it('returns true for a POST to "dataValues"', () => {
|
|
6
|
-
expect((0, _multipartFormDataMatchers.isDataValue)('create', {
|
|
7
|
-
resource: 'dataValues'
|
|
8
|
-
})).toBe(true);
|
|
9
|
-
});
|
|
10
|
-
it('returns true for a POST to "dataValues/file"', () => {
|
|
11
|
-
expect((0, _multipartFormDataMatchers.isDataValue)('create', {
|
|
12
|
-
resource: 'dataValues/file'
|
|
13
|
-
})).toBe(true);
|
|
14
|
-
});
|
|
15
|
-
it('returns false for a POST to a different resource', () => {
|
|
16
|
-
expect((0, _multipartFormDataMatchers.isDataValue)('create', {
|
|
17
|
-
resource: 'somethingElse'
|
|
18
|
-
})).toBe(false);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
describe('isFileResourceUpload', () => {
|
|
22
|
-
it('returns true for a POST to "fileResources"', () => {
|
|
23
|
-
expect((0, _multipartFormDataMatchers.isFileResourceUpload)('create', {
|
|
24
|
-
resource: 'fileResources'
|
|
25
|
-
})).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
it('retuns false for a POST to a different resource', () => {
|
|
28
|
-
expect((0, _multipartFormDataMatchers.isFileResourceUpload)('create', {
|
|
29
|
-
resource: 'notFileResources'
|
|
30
|
-
})).toBe(false);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
describe('isMessageConversationAttachment', () => {
|
|
34
|
-
it('returns true for a POST to "messageConversations/attachments"', () => {
|
|
35
|
-
expect((0, _multipartFormDataMatchers.isMessageConversationAttachment)('create', {
|
|
36
|
-
resource: 'messageConversations/attachments'
|
|
37
|
-
})).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
it('retuns false for a POST to a different resource', () => {
|
|
40
|
-
expect((0, _multipartFormDataMatchers.isMessageConversationAttachment)('create', {
|
|
41
|
-
resource: 'messageConversations/notAttachments'
|
|
42
|
-
})).toBe(false);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
describe('isStaticContentUpload', () => {
|
|
46
|
-
it('returns true for a POST to "staticContent/logo_banner"', () => {
|
|
47
|
-
expect((0, _multipartFormDataMatchers.isStaticContentUpload)('create', {
|
|
48
|
-
resource: 'staticContent/logo_banner'
|
|
49
|
-
})).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
it('returns true for a POST to "staticContent/logo_front"', () => {
|
|
52
|
-
expect((0, _multipartFormDataMatchers.isStaticContentUpload)('create', {
|
|
53
|
-
resource: 'staticContent/logo_front'
|
|
54
|
-
})).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
it('returns false for a request to a different resource', () => {
|
|
57
|
-
expect((0, _multipartFormDataMatchers.isStaticContentUpload)('create', {
|
|
58
|
-
resource: 'staticContent/no_logo'
|
|
59
|
-
})).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
describe('isAppInstall', () => {
|
|
63
|
-
it('returns true for a POST to "apps"', () => {
|
|
64
|
-
expect((0, _multipartFormDataMatchers.isAppInstall)('create', {
|
|
65
|
-
resource: 'apps'
|
|
66
|
-
})).toBe(true);
|
|
67
|
-
});
|
|
68
|
-
it('retuns false for a POST to a different resource', () => {
|
|
69
|
-
expect((0, _multipartFormDataMatchers.isAppInstall)('create', {
|
|
70
|
-
resource: 'notApps'
|
|
71
|
-
})).toBe(false);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.requestHeadersForContentType = exports.requestContentType = exports.requestBodyForContentType = void 0;
|
|
7
|
-
var multipartFormDataMatchers = _interopRequireWildcard(require("./multipartFormDataMatchers"));
|
|
8
|
-
var textPlainMatchers = _interopRequireWildcard(require("./textPlainMatchers"));
|
|
9
|
-
var xWwwFormUrlencodedMatchers = _interopRequireWildcard(require("./xWwwFormUrlencodedMatchers"));
|
|
10
|
-
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
11
|
-
const resourceExpectsTextPlain = (type, query) => Object.values(textPlainMatchers).some(textPlainMatcher => textPlainMatcher(type, query));
|
|
12
|
-
const resourceExpectsMultipartFormData = (type, query) => Object.values(multipartFormDataMatchers).some(multipartFormDataMatcher => multipartFormDataMatcher(type, query));
|
|
13
|
-
const resourceExpectsXWwwFormUrlencoded = (type, query) => Object.values(xWwwFormUrlencodedMatchers).some(xWwwFormUrlencodedMatcher => xWwwFormUrlencodedMatcher(type, query));
|
|
14
|
-
const convertData = (data, initialValue) => {
|
|
15
|
-
const dataEntries = Object.entries(data);
|
|
16
|
-
if (dataEntries.length === 0) {
|
|
17
|
-
throw new Error(`Could not convert data to ${initialValue.constructor.name}: object does not have own enumerable string-keyed properties`);
|
|
18
|
-
}
|
|
19
|
-
return dataEntries.reduce((convertedData, _ref) => {
|
|
20
|
-
let [key, value] = _ref;
|
|
21
|
-
convertedData.append(key, value);
|
|
22
|
-
return convertedData;
|
|
23
|
-
}, initialValue);
|
|
24
|
-
};
|
|
25
|
-
const requestContentType = (type, query) => {
|
|
26
|
-
if (!query.data) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
if (type === 'json-patch') {
|
|
30
|
-
return 'application/json-patch+json';
|
|
31
|
-
}
|
|
32
|
-
if (resourceExpectsTextPlain(type, query)) {
|
|
33
|
-
return 'text/plain';
|
|
34
|
-
}
|
|
35
|
-
if (resourceExpectsMultipartFormData(type, query)) {
|
|
36
|
-
return 'multipart/form-data';
|
|
37
|
-
}
|
|
38
|
-
if (resourceExpectsXWwwFormUrlencoded(type, query)) {
|
|
39
|
-
return 'application/x-www-form-urlencoded';
|
|
40
|
-
}
|
|
41
|
-
return 'application/json';
|
|
42
|
-
};
|
|
43
|
-
exports.requestContentType = requestContentType;
|
|
44
|
-
const requestHeadersForContentType = contentType => {
|
|
45
|
-
/*
|
|
46
|
-
* Explicitely setting Content-Type to 'multipart/form-data' produces
|
|
47
|
-
* a "multipart boundary not found" error. By not setting a Content-Type
|
|
48
|
-
* the browser will correctly set it for us and also apply multipart
|
|
49
|
-
* boundaries if the request body is an instance of FormData
|
|
50
|
-
* See https://stackoverflow.com/a/39281156/1143502
|
|
51
|
-
*/
|
|
52
|
-
if (!contentType || contentType === 'multipart/form-data') {
|
|
53
|
-
return undefined;
|
|
54
|
-
}
|
|
55
|
-
return {
|
|
56
|
-
'Content-Type': contentType
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
exports.requestHeadersForContentType = requestHeadersForContentType;
|
|
60
|
-
const requestBodyForContentType = (contentType, _ref2) => {
|
|
61
|
-
let {
|
|
62
|
-
data
|
|
63
|
-
} = _ref2;
|
|
64
|
-
if (typeof data === 'undefined') {
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
if (contentType === 'application/json' || contentType === 'application/json-patch+json') {
|
|
68
|
-
return JSON.stringify(data);
|
|
69
|
-
}
|
|
70
|
-
if (contentType === 'multipart/form-data') {
|
|
71
|
-
return convertData(data, new FormData());
|
|
72
|
-
}
|
|
73
|
-
if (contentType === 'application/x-www-form-urlencoded') {
|
|
74
|
-
return convertData(data, new URLSearchParams());
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// 'text/plain'
|
|
78
|
-
return data;
|
|
79
|
-
};
|
|
80
|
-
exports.requestBodyForContentType = requestBodyForContentType;
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _requestContentType = require("./requestContentType");
|
|
4
|
-
describe('requestContentType', () => {
|
|
5
|
-
it('returns "application/json" for a normal resource', () => {
|
|
6
|
-
expect((0, _requestContentType.requestContentType)('create', {
|
|
7
|
-
resource: 'test',
|
|
8
|
-
data: 'test'
|
|
9
|
-
})).toBe('application/json');
|
|
10
|
-
});
|
|
11
|
-
it('returns "application/json-patch+json" when the fetch type is "json-patch"', () => {
|
|
12
|
-
expect((0, _requestContentType.requestContentType)('json-patch', {
|
|
13
|
-
resource: 'test',
|
|
14
|
-
data: 'test'
|
|
15
|
-
})).toBe('application/json-patch+json');
|
|
16
|
-
});
|
|
17
|
-
it('returns "multipart/form-data" for a specific resource that expects it', () => {
|
|
18
|
-
expect((0, _requestContentType.requestContentType)('create', {
|
|
19
|
-
resource: 'fileResources',
|
|
20
|
-
data: 'test'
|
|
21
|
-
})).toBe('multipart/form-data');
|
|
22
|
-
});
|
|
23
|
-
it('returns "text/plain" for a specific resource that expects it', () => {
|
|
24
|
-
expect((0, _requestContentType.requestContentType)('create', {
|
|
25
|
-
resource: 'messageConversations/feedback',
|
|
26
|
-
data: 'test'
|
|
27
|
-
})).toBe('text/plain');
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
describe('requestHeadersForContentType', () => {
|
|
31
|
-
it('returns undefined if contentType is null', () => {
|
|
32
|
-
expect((0, _requestContentType.requestHeadersForContentType)(null)).toBe(undefined);
|
|
33
|
-
});
|
|
34
|
-
it('returns undefined if contentType is "multipart/form-data"', () => {
|
|
35
|
-
expect((0, _requestContentType.requestHeadersForContentType)('multipart/form-data')).toBe(undefined);
|
|
36
|
-
});
|
|
37
|
-
it('returns a headers object with the contentType for "application/json"', () => {
|
|
38
|
-
expect((0, _requestContentType.requestHeadersForContentType)('application/json')).toEqual({
|
|
39
|
-
'Content-Type': 'application/json'
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
it('returns a headers object with the contentType for "text/plain"', () => {
|
|
43
|
-
expect((0, _requestContentType.requestHeadersForContentType)('text/plain')).toEqual({
|
|
44
|
-
'Content-Type': 'text/plain'
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
describe('requestBodyForContentType', () => {
|
|
49
|
-
it('returns undefined if data is undefined', () => {
|
|
50
|
-
expect((0, _requestContentType.requestBodyForContentType)('application/json', {
|
|
51
|
-
resource: 'test'
|
|
52
|
-
})).toBe(undefined);
|
|
53
|
-
});
|
|
54
|
-
it('JSON stringifies the data if contentType is "application/json"', () => {
|
|
55
|
-
const dataIn = {
|
|
56
|
-
a: 'AAAA',
|
|
57
|
-
b: 1,
|
|
58
|
-
c: true
|
|
59
|
-
};
|
|
60
|
-
const dataOut = JSON.stringify(dataIn);
|
|
61
|
-
expect((0, _requestContentType.requestBodyForContentType)('application/json', {
|
|
62
|
-
resource: 'test',
|
|
63
|
-
data: dataIn
|
|
64
|
-
})).toBe(dataOut);
|
|
65
|
-
});
|
|
66
|
-
it('converts to FormData if contentType is "multipart/form-data"', () => {
|
|
67
|
-
const file = new File(['foo'], 'foo.txt', {
|
|
68
|
-
type: 'text/plain'
|
|
69
|
-
});
|
|
70
|
-
const data = {
|
|
71
|
-
a: 'AAA',
|
|
72
|
-
file
|
|
73
|
-
};
|
|
74
|
-
const result = (0, _requestContentType.requestBodyForContentType)('multipart/form-data', {
|
|
75
|
-
resource: 'test',
|
|
76
|
-
data
|
|
77
|
-
});
|
|
78
|
-
expect(result instanceof FormData).toBe(true);
|
|
79
|
-
expect(result.get('a')).toBe('AAA');
|
|
80
|
-
expect(result.get('file')).toBe(file);
|
|
81
|
-
});
|
|
82
|
-
it('throws an error if contentType is "multipart/form-data" and data does have own string-keyd properties', () => {
|
|
83
|
-
expect(() => {
|
|
84
|
-
(0, _requestContentType.requestBodyForContentType)('multipart/form-data', {
|
|
85
|
-
resource: 'test',
|
|
86
|
-
data: new File(['foo'], 'foo.txt', {
|
|
87
|
-
type: 'text/plain'
|
|
88
|
-
})
|
|
89
|
-
});
|
|
90
|
-
}).toThrowErrorMatchingInlineSnapshot(`"Could not convert data to FormData: object does not have own enumerable string-keyed properties"`);
|
|
91
|
-
});
|
|
92
|
-
it('converts to URLSearchParams if contentType is "application/x-www-form-urlencoded"', () => {
|
|
93
|
-
const data = {
|
|
94
|
-
a: 'AAA'
|
|
95
|
-
};
|
|
96
|
-
const result = (0, _requestContentType.requestBodyForContentType)('application/x-www-form-urlencoded', {
|
|
97
|
-
resource: 'test',
|
|
98
|
-
data
|
|
99
|
-
});
|
|
100
|
-
expect(result instanceof URLSearchParams).toBe(true);
|
|
101
|
-
expect(result.get('a')).toBe('AAA');
|
|
102
|
-
});
|
|
103
|
-
it('throws an error if contentType is "application/x-www-form-urlencoded" and data does have own string-keyd properties', () => {
|
|
104
|
-
expect(() => {
|
|
105
|
-
(0, _requestContentType.requestBodyForContentType)('application/x-www-form-urlencoded', {
|
|
106
|
-
resource: 'test',
|
|
107
|
-
data: new File(['foo'], 'foo.txt', {
|
|
108
|
-
type: 'text/plain'
|
|
109
|
-
})
|
|
110
|
-
});
|
|
111
|
-
}).toThrowErrorMatchingInlineSnapshot(`"Could not convert data to URLSearchParams: object does not have own enumerable string-keyed properties"`);
|
|
112
|
-
});
|
|
113
|
-
it('returns the data as received if contentType is "text/plain"', () => {
|
|
114
|
-
const data = 'Something';
|
|
115
|
-
expect((0, _requestContentType.requestBodyForContentType)('text/plain', {
|
|
116
|
-
resource: 'messageConversations/feedback',
|
|
117
|
-
data
|
|
118
|
-
})).toBe(data);
|
|
119
|
-
});
|
|
120
|
-
});
|