@dhis2/app-service-data 3.2.9 → 3.4.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/engine/helpers/getMutationFetchType.test.js +5 -0
- package/build/cjs/engine/helpers/validate.js +5 -1
- package/build/cjs/engine/helpers/validate.test.js +8 -0
- package/build/cjs/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.js +10 -2
- package/build/cjs/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.test.js +17 -0
- package/build/cjs/links/RestAPILink/queryToRequestOptions/requestContentType.js +5 -1
- package/build/cjs/links/RestAPILink/queryToRequestOptions/requestContentType.test.js +6 -0
- package/build/cjs/links/RestAPILink/queryToRequestOptions.js +4 -0
- package/build/cjs/links/RestAPILink/queryToRequestOptions.test.js +10 -0
- package/build/cjs/links/RestAPILink/queryToResourcePath.js +15 -2
- package/build/cjs/links/RestAPILink/queryToResourcePath.test.js +48 -15
- package/build/cjs/links/RestAPILink.js +9 -12
- package/build/es/engine/helpers/getMutationFetchType.test.js +5 -0
- package/build/es/engine/helpers/validate.js +5 -1
- package/build/es/engine/helpers/validate.test.js +8 -0
- package/build/es/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.js +6 -1
- package/build/es/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.test.js +18 -1
- package/build/es/links/RestAPILink/queryToRequestOptions/requestContentType.js +5 -1
- package/build/es/links/RestAPILink/queryToRequestOptions/requestContentType.test.js +6 -0
- package/build/es/links/RestAPILink/queryToRequestOptions.js +4 -0
- package/build/es/links/RestAPILink/queryToRequestOptions.test.js +10 -0
- package/build/es/links/RestAPILink/queryToResourcePath.js +15 -2
- package/build/es/links/RestAPILink/queryToResourcePath.test.js +48 -15
- package/build/es/links/RestAPILink.js +9 -12
- package/build/types/engine/types/ExecuteOptions.d.ts +1 -1
- package/build/types/engine/types/Mutation.d.ts +2 -2
- package/build/types/links/RestAPILink/queryToRequestOptions/multipartFormDataMatchers.d.ts +1 -0
- package/build/types/links/RestAPILink/queryToRequestOptions/requestContentType.d.ts +4 -6
- package/build/types/links/RestAPILink/queryToResourcePath.d.ts +2 -1
- package/build/types/links/RestAPILink.d.ts +5 -8
- package/package.json +2 -2
|
@@ -14,6 +14,11 @@ describe('getMutationFetchType', () => {
|
|
|
14
14
|
resource: 'test',
|
|
15
15
|
id: 'id'
|
|
16
16
|
})).toBe('delete');
|
|
17
|
+
expect((0, _getMutationFetchType.getMutationFetchType)({
|
|
18
|
+
type: 'json-patch',
|
|
19
|
+
resource: 'test',
|
|
20
|
+
data: {}
|
|
21
|
+
})).toBe('json-patch');
|
|
17
22
|
});
|
|
18
23
|
it('should return `replace` for non-partial `update`', () => {
|
|
19
24
|
expect((0, _getMutationFetchType.getMutationFetchType)({
|
|
@@ -8,7 +8,7 @@ exports.validateResourceQuery = exports.validateResourceQueries = exports.getRes
|
|
|
8
8
|
var _InvalidQueryError = require("../types/InvalidQueryError");
|
|
9
9
|
|
|
10
10
|
const validQueryKeys = ['resource', 'id', 'params', 'data'];
|
|
11
|
-
const validTypes = ['read', 'create', 'update', 'replace', 'delete'];
|
|
11
|
+
const validTypes = ['read', 'create', 'update', 'replace', 'delete', 'json-patch'];
|
|
12
12
|
|
|
13
13
|
const getResourceQueryErrors = (type, query) => {
|
|
14
14
|
if (!validTypes.includes(type)) {
|
|
@@ -41,6 +41,10 @@ const getResourceQueryErrors = (type, query) => {
|
|
|
41
41
|
errors.push("Mutation type 'delete' does not support property 'data'");
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
if (type === 'json-patch' && !Array.isArray(query.data)) {
|
|
45
|
+
errors.push("Mutation type 'json-patch' requires property 'data' to be of type Array");
|
|
46
|
+
}
|
|
47
|
+
|
|
44
48
|
const invalidKeys = Object.keys(query).filter(k => !validQueryKeys.includes(k));
|
|
45
49
|
invalidKeys.forEach(k => {
|
|
46
50
|
errors.push("Property ".concat(k, " is not supported"));
|
|
@@ -76,6 +76,14 @@ describe('query validation', () => {
|
|
|
76
76
|
expect(errors).toHaveLength(1);
|
|
77
77
|
expect(errors).toMatchInlineSnapshot("\n Array [\n \"Mutation type 'delete' does not support property 'data'\",\n ]\n ");
|
|
78
78
|
});
|
|
79
|
+
it('should fail if query is json-patch mutation with non-array data prop', () => {
|
|
80
|
+
const errors = (0, _validate.getResourceQueryErrors)('json-patch', {
|
|
81
|
+
resource: 'metadata',
|
|
82
|
+
data: {}
|
|
83
|
+
});
|
|
84
|
+
expect(errors).toHaveLength(1);
|
|
85
|
+
expect(errors).toMatchInlineSnapshot("\n Array [\n \"Mutation type 'json-patch' requires property 'data' to be of type Array\",\n ]\n ");
|
|
86
|
+
});
|
|
79
87
|
it('should fail if unrecognized keys are passed to query', () => {
|
|
80
88
|
const errors = (0, _validate.getResourceQueryErrors)('update', {
|
|
81
89
|
resource: 'indicators',
|
|
@@ -3,14 +3,22 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.isSvgConversion = exports.isAppInstall = exports.isStaticContentUpload = exports.isMessageConversationAttachment = exports.isFileResourceUpload = void 0;
|
|
6
|
+
exports.isSvgConversion = exports.isAppInstall = exports.isStaticContentUpload = exports.isMessageConversationAttachment = exports.isFileResourceUpload = exports.isDataValue = void 0;
|
|
7
7
|
|
|
8
8
|
/*
|
|
9
9
|
* Requests that expect a "multipart/form-data" Content-Type have been collected by scanning
|
|
10
10
|
* the developer documentation:
|
|
11
11
|
* https://docs.dhis2.org/master/en/developer/html/dhis2_developer_manual_full.html
|
|
12
12
|
*/
|
|
13
|
-
//
|
|
13
|
+
// Post to 'dataValues' (send/update a data value; endpoint doesn't support JSON)
|
|
14
|
+
// For file-uploads too
|
|
15
|
+
const isDataValue = (type, {
|
|
16
|
+
resource
|
|
17
|
+
}) => type === 'create' && (resource === 'dataValues' || resource === 'dataValues/file'); // POST to 'fileResources' (upload a file resource)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
exports.isDataValue = isDataValue;
|
|
21
|
+
|
|
14
22
|
const isFileResourceUpload = (type, {
|
|
15
23
|
resource
|
|
16
24
|
}) => type === 'create' && resource === 'fileResources'; // POST to 'messageConversations/attachments' (upload a message conversation attachment)
|
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
var _multipartFormDataMatchers = require("./multipartFormDataMatchers");
|
|
4
4
|
|
|
5
|
+
describe('isDataValue', () => {
|
|
6
|
+
it('returns true for a POST to "dataValues"', () => {
|
|
7
|
+
expect((0, _multipartFormDataMatchers.isDataValue)('create', {
|
|
8
|
+
resource: 'dataValues'
|
|
9
|
+
})).toEqual(true);
|
|
10
|
+
});
|
|
11
|
+
it('returns true for a POST to "dataValues/file"', () => {
|
|
12
|
+
expect((0, _multipartFormDataMatchers.isDataValue)('create', {
|
|
13
|
+
resource: 'dataValues/file'
|
|
14
|
+
})).toEqual(true);
|
|
15
|
+
});
|
|
16
|
+
it('returns false for a POST to a different resource', () => {
|
|
17
|
+
expect((0, _multipartFormDataMatchers.isDataValue)('create', {
|
|
18
|
+
resource: 'somethingElse'
|
|
19
|
+
})).toEqual(false);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
5
22
|
describe('isFileResourceUpload', () => {
|
|
6
23
|
it('returns true for a POST to "fileResources"', () => {
|
|
7
24
|
expect((0, _multipartFormDataMatchers.isFileResourceUpload)('create', {
|
|
@@ -38,6 +38,10 @@ const requestContentType = (type, query) => {
|
|
|
38
38
|
return null;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
if (type === 'json-patch') {
|
|
42
|
+
return 'application/json-patch+json';
|
|
43
|
+
}
|
|
44
|
+
|
|
41
45
|
if (resourceExpectsTextPlain(type, query)) {
|
|
42
46
|
return 'text/plain';
|
|
43
47
|
}
|
|
@@ -77,7 +81,7 @@ const requestBodyForContentType = (contentType, {
|
|
|
77
81
|
return undefined;
|
|
78
82
|
}
|
|
79
83
|
|
|
80
|
-
if (contentType === 'application/json') {
|
|
84
|
+
if (contentType === 'application/json' || contentType === 'application/json-patch+json') {
|
|
81
85
|
return JSON.stringify(data);
|
|
82
86
|
}
|
|
83
87
|
|
|
@@ -9,6 +9,12 @@ describe('requestContentType', () => {
|
|
|
9
9
|
data: 'test'
|
|
10
10
|
})).toEqual('application/json');
|
|
11
11
|
});
|
|
12
|
+
it('returns "application/json-patch+json" when the fetch type is "json-patch"', () => {
|
|
13
|
+
expect((0, _requestContentType.requestContentType)('json-patch', {
|
|
14
|
+
resource: 'test',
|
|
15
|
+
data: 'test'
|
|
16
|
+
})).toEqual('application/json-patch+json');
|
|
17
|
+
});
|
|
12
18
|
it('returns "multipart/form-data" for a specific resource that expects it', () => {
|
|
13
19
|
expect((0, _requestContentType.requestContentType)('create', {
|
|
14
20
|
resource: 'fileResources',
|
|
@@ -16,6 +16,7 @@ const getMethod = type => {
|
|
|
16
16
|
return 'GET';
|
|
17
17
|
|
|
18
18
|
case 'update':
|
|
19
|
+
case 'json-patch':
|
|
19
20
|
return 'PATCH';
|
|
20
21
|
|
|
21
22
|
case 'replace':
|
|
@@ -23,6 +24,9 @@ const getMethod = type => {
|
|
|
23
24
|
|
|
24
25
|
case 'delete':
|
|
25
26
|
return 'DELETE';
|
|
27
|
+
|
|
28
|
+
default:
|
|
29
|
+
throw new Error("Unknown type ".concat(type));
|
|
26
30
|
}
|
|
27
31
|
};
|
|
28
32
|
|
|
@@ -29,6 +29,16 @@ describe('queryToRequestOptions', () => {
|
|
|
29
29
|
});
|
|
30
30
|
expect(options).toMatchInlineSnapshot("\n Object {\n \"body\": \"{\\\"answer\\\":42,\\\"foo\\\":\\\"bar\\\"}\",\n \"headers\": Object {\n \"Content-Type\": \"application/json\",\n },\n \"method\": \"PATCH\",\n \"signal\": undefined,\n }\n ");
|
|
31
31
|
});
|
|
32
|
+
it('should return a valid Fetch option object for json-patch request', () => {
|
|
33
|
+
const options = (0, _queryToRequestOptions.queryToRequestOptions)('json-patch', {
|
|
34
|
+
resource: 'test',
|
|
35
|
+
data: {
|
|
36
|
+
answer: 42,
|
|
37
|
+
foo: 'bar'
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
expect(options).toMatchInlineSnapshot("\n Object {\n \"body\": \"{\\\"answer\\\":42,\\\"foo\\\":\\\"bar\\\"}\",\n \"headers\": Object {\n \"Content-Type\": \"application/json-patch+json\",\n },\n \"method\": \"PATCH\",\n \"signal\": undefined,\n }\n ");
|
|
41
|
+
});
|
|
32
42
|
it('should return a valid Fetch option object for replace request', () => {
|
|
33
43
|
const options = (0, _queryToRequestOptions.queryToRequestOptions)('replace', {
|
|
34
44
|
resource: 'test',
|
|
@@ -63,13 +63,26 @@ const isAction = resource => resource.startsWith(actionPrefix);
|
|
|
63
63
|
|
|
64
64
|
const makeActionPath = resource => (0, _path.joinPath)('dhis-web-commons', "".concat(resource.substr(actionPrefix.length), ".action"));
|
|
65
65
|
|
|
66
|
-
const
|
|
66
|
+
const skipApiVersion = (resource, config) => {
|
|
67
|
+
if (resource === 'tracker') {
|
|
68
|
+
var _config$serverVersion, _config$serverVersion2;
|
|
69
|
+
|
|
70
|
+
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) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return false;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const queryToResourcePath = (link, query, type) => {
|
|
67
79
|
const {
|
|
68
80
|
resource,
|
|
69
81
|
id,
|
|
70
82
|
params = {}
|
|
71
83
|
} = query;
|
|
72
|
-
const
|
|
84
|
+
const apiBase = skipApiVersion(resource, link.config) ? link.unversionedApiPath : link.versionedApiPath;
|
|
85
|
+
const base = isAction(resource) ? makeActionPath(resource) : (0, _path.joinPath)(apiBase, resource, id);
|
|
73
86
|
(0, _validateQuery.validateResourceQuery)(query, type);
|
|
74
87
|
|
|
75
88
|
if (Object.keys(params).length) {
|
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _RestAPILink = require("../RestAPILink");
|
|
4
|
+
|
|
3
5
|
var _queryToResourcePath = require("./queryToResourcePath");
|
|
4
6
|
|
|
5
|
-
const
|
|
7
|
+
const createLink = config => new _RestAPILink.RestAPILink(config);
|
|
8
|
+
|
|
9
|
+
const defaultConfig = {
|
|
10
|
+
basePath: '<base>',
|
|
11
|
+
apiVersion: '37',
|
|
12
|
+
serverVersion: {
|
|
13
|
+
major: 2,
|
|
14
|
+
minor: 37,
|
|
15
|
+
patch: 11
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const link = createLink(defaultConfig);
|
|
19
|
+
const apiPath = link.versionedApiPath;
|
|
6
20
|
const actionPrefix = "dhis-web-commons/";
|
|
7
21
|
const actionPostfix = '.action';
|
|
8
22
|
describe('queryToResourcePath', () => {
|
|
@@ -11,7 +25,7 @@ describe('queryToResourcePath', () => {
|
|
|
11
25
|
const query = {
|
|
12
26
|
resource: 'action::test'
|
|
13
27
|
};
|
|
14
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
28
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(actionPrefix, "test").concat(actionPostfix));
|
|
15
29
|
});
|
|
16
30
|
it('should return action URL with a simple querystring if query parameters are passed', () => {
|
|
17
31
|
const query = {
|
|
@@ -20,7 +34,7 @@ describe('queryToResourcePath', () => {
|
|
|
20
34
|
key: 'value'
|
|
21
35
|
}
|
|
22
36
|
};
|
|
23
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
37
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(actionPrefix, "test").concat(actionPostfix, "?key=value"));
|
|
24
38
|
});
|
|
25
39
|
});
|
|
26
40
|
describe('resource with dot', () => {
|
|
@@ -28,14 +42,14 @@ describe('queryToResourcePath', () => {
|
|
|
28
42
|
const query = {
|
|
29
43
|
resource: 'svg.pdf'
|
|
30
44
|
};
|
|
31
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
45
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/svg.pdf"));
|
|
32
46
|
});
|
|
33
47
|
});
|
|
34
48
|
it('should return resource url with no querystring if not query parameters are passed', () => {
|
|
35
49
|
const query = {
|
|
36
50
|
resource: 'test'
|
|
37
51
|
};
|
|
38
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
52
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test"));
|
|
39
53
|
});
|
|
40
54
|
it('should return resource url and singular parameter separated by ?', () => {
|
|
41
55
|
const query = {
|
|
@@ -44,7 +58,7 @@ describe('queryToResourcePath', () => {
|
|
|
44
58
|
key: 'value'
|
|
45
59
|
}
|
|
46
60
|
};
|
|
47
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
61
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value"));
|
|
48
62
|
});
|
|
49
63
|
it('should return resource url and multiple parameters separated by ? and &', () => {
|
|
50
64
|
const query = {
|
|
@@ -54,7 +68,7 @@ describe('queryToResourcePath', () => {
|
|
|
54
68
|
param: 'value2'
|
|
55
69
|
}
|
|
56
70
|
};
|
|
57
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
71
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value¶m=value2"));
|
|
58
72
|
});
|
|
59
73
|
it('should url encode special characters in query keys', () => {
|
|
60
74
|
const query = {
|
|
@@ -63,7 +77,7 @@ describe('queryToResourcePath', () => {
|
|
|
63
77
|
'key=42&val': 'value'
|
|
64
78
|
}
|
|
65
79
|
};
|
|
66
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
80
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key%3D42%26val=value"));
|
|
67
81
|
});
|
|
68
82
|
it('should url encode special characters in string parameters', () => {
|
|
69
83
|
const query = {
|
|
@@ -73,7 +87,7 @@ describe('queryToResourcePath', () => {
|
|
|
73
87
|
param: 'value2&& 53'
|
|
74
88
|
}
|
|
75
89
|
};
|
|
76
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
90
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value%3F%3D42¶m=value2%26%26%2053"));
|
|
77
91
|
});
|
|
78
92
|
it('should support numeric (integer and float) parameters', () => {
|
|
79
93
|
const query = {
|
|
@@ -83,7 +97,7 @@ describe('queryToResourcePath', () => {
|
|
|
83
97
|
param: 193.75
|
|
84
98
|
}
|
|
85
99
|
};
|
|
86
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
100
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=42¶m=193.75"));
|
|
87
101
|
});
|
|
88
102
|
it('should support boolean parameters', () => {
|
|
89
103
|
const query = {
|
|
@@ -93,7 +107,7 @@ describe('queryToResourcePath', () => {
|
|
|
93
107
|
someflag: true
|
|
94
108
|
}
|
|
95
109
|
};
|
|
96
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
110
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=42&someflag=true"));
|
|
97
111
|
});
|
|
98
112
|
it('should join array parameters with commas', () => {
|
|
99
113
|
const query = {
|
|
@@ -102,7 +116,7 @@ describe('queryToResourcePath', () => {
|
|
|
102
116
|
key: ['asdf', 123]
|
|
103
117
|
}
|
|
104
118
|
};
|
|
105
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
119
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?key=asdf,123"));
|
|
106
120
|
});
|
|
107
121
|
it('should include multiple filter parameters when array of filters provided', () => {
|
|
108
122
|
const query = {
|
|
@@ -111,7 +125,7 @@ describe('queryToResourcePath', () => {
|
|
|
111
125
|
filter: ['asdf', 123]
|
|
112
126
|
}
|
|
113
127
|
};
|
|
114
|
-
expect((0, _queryToResourcePath.queryToResourcePath)(
|
|
128
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(apiPath, "/test?filter=asdf&filter=123"));
|
|
115
129
|
});
|
|
116
130
|
it('should NOT YET support name-aliased parameters', () => {
|
|
117
131
|
const query = {
|
|
@@ -122,7 +136,7 @@ describe('queryToResourcePath', () => {
|
|
|
122
136
|
}
|
|
123
137
|
}
|
|
124
138
|
};
|
|
125
|
-
expect(() => (0, _queryToResourcePath.queryToResourcePath)(
|
|
139
|
+
expect(() => (0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toThrow();
|
|
126
140
|
});
|
|
127
141
|
it('should throw if passed something crazy like a function', () => {
|
|
128
142
|
const query = {
|
|
@@ -131,6 +145,25 @@ describe('queryToResourcePath', () => {
|
|
|
131
145
|
key: a => a
|
|
132
146
|
}
|
|
133
147
|
};
|
|
134
|
-
expect(() => (0, _queryToResourcePath.queryToResourcePath)(
|
|
148
|
+
expect(() => (0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toThrow();
|
|
149
|
+
});
|
|
150
|
+
it('should return an unversioned endpoint for the new tracker importer (in version 2.37)', () => {
|
|
151
|
+
const query = {
|
|
152
|
+
resource: 'tracker'
|
|
153
|
+
};
|
|
154
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(link, query, 'read')).toBe("".concat(link.unversionedApiPath, "/tracker"));
|
|
155
|
+
});
|
|
156
|
+
it('should return a VERSIONED endpoint for the new tracker importer (in version 2.38)', () => {
|
|
157
|
+
const query = {
|
|
158
|
+
resource: 'tracker'
|
|
159
|
+
};
|
|
160
|
+
const v38config = { ...defaultConfig,
|
|
161
|
+
serverVersion: {
|
|
162
|
+
major: 2,
|
|
163
|
+
minor: 38,
|
|
164
|
+
patch: 0
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
expect((0, _queryToResourcePath.queryToResourcePath)(createLink(v38config), query, 'read')).toBe("".concat(link.versionedApiPath, "/tracker"));
|
|
135
168
|
});
|
|
136
169
|
});
|
|
@@ -16,29 +16,26 @@ var _queryToResourcePath = require("./RestAPILink/queryToResourcePath");
|
|
|
16
16
|
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; }
|
|
17
17
|
|
|
18
18
|
class RestAPILink {
|
|
19
|
-
constructor({
|
|
20
|
-
|
|
21
|
-
apiVersion
|
|
22
|
-
}) {
|
|
23
|
-
_defineProperty(this, "apiPath", void 0);
|
|
19
|
+
constructor(config) {
|
|
20
|
+
_defineProperty(this, "config", void 0);
|
|
24
21
|
|
|
25
|
-
_defineProperty(this, "
|
|
22
|
+
_defineProperty(this, "versionedApiPath", void 0);
|
|
26
23
|
|
|
27
|
-
_defineProperty(this, "
|
|
24
|
+
_defineProperty(this, "unversionedApiPath", void 0);
|
|
28
25
|
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.versionedApiPath = (0, _path.joinPath)('api', String(config.apiVersion));
|
|
28
|
+
this.unversionedApiPath = (0, _path.joinPath)('api');
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
fetch(path, options) {
|
|
35
|
-
return (0, _fetchData.fetchData)((0, _path.joinPath)(this.baseUrl, path), options);
|
|
32
|
+
return (0, _fetchData.fetchData)((0, _path.joinPath)(this.config.baseUrl, path), options);
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
executeResourceQuery(type, query, {
|
|
39
36
|
signal
|
|
40
37
|
}) {
|
|
41
|
-
return this.fetch((0, _queryToResourcePath.queryToResourcePath)(this
|
|
38
|
+
return this.fetch((0, _queryToResourcePath.queryToResourcePath)(this, query, type), (0, _queryToRequestOptions.queryToRequestOptions)(type, query, signal));
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
}
|
|
@@ -11,6 +11,11 @@ describe('getMutationFetchType', () => {
|
|
|
11
11
|
resource: 'test',
|
|
12
12
|
id: 'id'
|
|
13
13
|
})).toBe('delete');
|
|
14
|
+
expect(getMutationFetchType({
|
|
15
|
+
type: 'json-patch',
|
|
16
|
+
resource: 'test',
|
|
17
|
+
data: {}
|
|
18
|
+
})).toBe('json-patch');
|
|
14
19
|
});
|
|
15
20
|
it('should return `replace` for non-partial `update`', () => {
|
|
16
21
|
expect(getMutationFetchType({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InvalidQueryError } from '../types/InvalidQueryError';
|
|
2
2
|
const validQueryKeys = ['resource', 'id', 'params', 'data'];
|
|
3
|
-
const validTypes = ['read', 'create', 'update', 'replace', 'delete'];
|
|
3
|
+
const validTypes = ['read', 'create', 'update', 'replace', 'delete', 'json-patch'];
|
|
4
4
|
export const getResourceQueryErrors = (type, query) => {
|
|
5
5
|
if (!validTypes.includes(type)) {
|
|
6
6
|
return ["Unknown query or mutation type ".concat(type)];
|
|
@@ -32,6 +32,10 @@ export const getResourceQueryErrors = (type, query) => {
|
|
|
32
32
|
errors.push("Mutation type 'delete' does not support property 'data'");
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
if (type === 'json-patch' && !Array.isArray(query.data)) {
|
|
36
|
+
errors.push("Mutation type 'json-patch' requires property 'data' to be of type Array");
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
const invalidKeys = Object.keys(query).filter(k => !validQueryKeys.includes(k));
|
|
36
40
|
invalidKeys.forEach(k => {
|
|
37
41
|
errors.push("Property ".concat(k, " is not supported"));
|
|
@@ -73,6 +73,14 @@ describe('query validation', () => {
|
|
|
73
73
|
expect(errors).toHaveLength(1);
|
|
74
74
|
expect(errors).toMatchInlineSnapshot("\n Array [\n \"Mutation type 'delete' does not support property 'data'\",\n ]\n ");
|
|
75
75
|
});
|
|
76
|
+
it('should fail if query is json-patch mutation with non-array data prop', () => {
|
|
77
|
+
const errors = getResourceQueryErrors('json-patch', {
|
|
78
|
+
resource: 'metadata',
|
|
79
|
+
data: {}
|
|
80
|
+
});
|
|
81
|
+
expect(errors).toHaveLength(1);
|
|
82
|
+
expect(errors).toMatchInlineSnapshot("\n Array [\n \"Mutation type 'json-patch' requires property 'data' to be of type Array\",\n ]\n ");
|
|
83
|
+
});
|
|
76
84
|
it('should fail if unrecognized keys are passed to query', () => {
|
|
77
85
|
const errors = getResourceQueryErrors('update', {
|
|
78
86
|
resource: 'indicators',
|
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
* the developer documentation:
|
|
4
4
|
* https://docs.dhis2.org/master/en/developer/html/dhis2_developer_manual_full.html
|
|
5
5
|
*/
|
|
6
|
-
//
|
|
6
|
+
// Post to 'dataValues' (send/update a data value; endpoint doesn't support JSON)
|
|
7
|
+
// For file-uploads too
|
|
8
|
+
export const isDataValue = (type, {
|
|
9
|
+
resource
|
|
10
|
+
}) => type === 'create' && (resource === 'dataValues' || resource === 'dataValues/file'); // POST to 'fileResources' (upload a file resource)
|
|
11
|
+
|
|
7
12
|
export const isFileResourceUpload = (type, {
|
|
8
13
|
resource
|
|
9
14
|
}) => type === 'create' && resource === 'fileResources'; // POST to 'messageConversations/attachments' (upload a message conversation attachment)
|
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
import { isFileResourceUpload, isMessageConversationAttachment, isStaticContentUpload, isAppInstall, isSvgConversion } from './multipartFormDataMatchers';
|
|
1
|
+
import { isFileResourceUpload, isMessageConversationAttachment, isStaticContentUpload, isAppInstall, isSvgConversion, isDataValue } from './multipartFormDataMatchers';
|
|
2
|
+
describe('isDataValue', () => {
|
|
3
|
+
it('returns true for a POST to "dataValues"', () => {
|
|
4
|
+
expect(isDataValue('create', {
|
|
5
|
+
resource: 'dataValues'
|
|
6
|
+
})).toEqual(true);
|
|
7
|
+
});
|
|
8
|
+
it('returns true for a POST to "dataValues/file"', () => {
|
|
9
|
+
expect(isDataValue('create', {
|
|
10
|
+
resource: 'dataValues/file'
|
|
11
|
+
})).toEqual(true);
|
|
12
|
+
});
|
|
13
|
+
it('returns false for a POST to a different resource', () => {
|
|
14
|
+
expect(isDataValue('create', {
|
|
15
|
+
resource: 'somethingElse'
|
|
16
|
+
})).toEqual(false);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
2
19
|
describe('isFileResourceUpload', () => {
|
|
3
20
|
it('returns true for a POST to "fileResources"', () => {
|
|
4
21
|
expect(isFileResourceUpload('create', {
|
|
@@ -25,6 +25,10 @@ export const requestContentType = (type, query) => {
|
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
if (type === 'json-patch') {
|
|
29
|
+
return 'application/json-patch+json';
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
if (resourceExpectsTextPlain(type, query)) {
|
|
29
33
|
return 'text/plain';
|
|
30
34
|
}
|
|
@@ -58,7 +62,7 @@ export const requestBodyForContentType = (contentType, {
|
|
|
58
62
|
return undefined;
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
if (contentType === 'application/json') {
|
|
65
|
+
if (contentType === 'application/json' || contentType === 'application/json-patch+json') {
|
|
62
66
|
return JSON.stringify(data);
|
|
63
67
|
}
|
|
64
68
|
|
|
@@ -6,6 +6,12 @@ describe('requestContentType', () => {
|
|
|
6
6
|
data: 'test'
|
|
7
7
|
})).toEqual('application/json');
|
|
8
8
|
});
|
|
9
|
+
it('returns "application/json-patch+json" when the fetch type is "json-patch"', () => {
|
|
10
|
+
expect(requestContentType('json-patch', {
|
|
11
|
+
resource: 'test',
|
|
12
|
+
data: 'test'
|
|
13
|
+
})).toEqual('application/json-patch+json');
|
|
14
|
+
});
|
|
9
15
|
it('returns "multipart/form-data" for a specific resource that expects it', () => {
|
|
10
16
|
expect(requestContentType('create', {
|
|
11
17
|
resource: 'fileResources',
|
|
@@ -9,6 +9,7 @@ const getMethod = type => {
|
|
|
9
9
|
return 'GET';
|
|
10
10
|
|
|
11
11
|
case 'update':
|
|
12
|
+
case 'json-patch':
|
|
12
13
|
return 'PATCH';
|
|
13
14
|
|
|
14
15
|
case 'replace':
|
|
@@ -16,6 +17,9 @@ const getMethod = type => {
|
|
|
16
17
|
|
|
17
18
|
case 'delete':
|
|
18
19
|
return 'DELETE';
|
|
20
|
+
|
|
21
|
+
default:
|
|
22
|
+
throw new Error("Unknown type ".concat(type));
|
|
19
23
|
}
|
|
20
24
|
};
|
|
21
25
|
|
|
@@ -26,6 +26,16 @@ describe('queryToRequestOptions', () => {
|
|
|
26
26
|
});
|
|
27
27
|
expect(options).toMatchInlineSnapshot("\n Object {\n \"body\": \"{\\\"answer\\\":42,\\\"foo\\\":\\\"bar\\\"}\",\n \"headers\": Object {\n \"Content-Type\": \"application/json\",\n },\n \"method\": \"PATCH\",\n \"signal\": undefined,\n }\n ");
|
|
28
28
|
});
|
|
29
|
+
it('should return a valid Fetch option object for json-patch request', () => {
|
|
30
|
+
const options = queryToRequestOptions('json-patch', {
|
|
31
|
+
resource: 'test',
|
|
32
|
+
data: {
|
|
33
|
+
answer: 42,
|
|
34
|
+
foo: 'bar'
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
expect(options).toMatchInlineSnapshot("\n Object {\n \"body\": \"{\\\"answer\\\":42,\\\"foo\\\":\\\"bar\\\"}\",\n \"headers\": Object {\n \"Content-Type\": \"application/json-patch+json\",\n },\n \"method\": \"PATCH\",\n \"signal\": undefined,\n }\n ");
|
|
38
|
+
});
|
|
29
39
|
it('should return a valid Fetch option object for replace request', () => {
|
|
30
40
|
const options = queryToRequestOptions('replace', {
|
|
31
41
|
resource: 'test',
|
|
@@ -55,13 +55,26 @@ const isAction = resource => resource.startsWith(actionPrefix);
|
|
|
55
55
|
|
|
56
56
|
const makeActionPath = resource => joinPath('dhis-web-commons', "".concat(resource.substr(actionPrefix.length), ".action"));
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
const skipApiVersion = (resource, config) => {
|
|
59
|
+
if (resource === 'tracker') {
|
|
60
|
+
var _config$serverVersion, _config$serverVersion2;
|
|
61
|
+
|
|
62
|
+
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) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return false;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const queryToResourcePath = (link, query, type) => {
|
|
59
71
|
const {
|
|
60
72
|
resource,
|
|
61
73
|
id,
|
|
62
74
|
params = {}
|
|
63
75
|
} = query;
|
|
64
|
-
const
|
|
76
|
+
const apiBase = skipApiVersion(resource, link.config) ? link.unversionedApiPath : link.versionedApiPath;
|
|
77
|
+
const base = isAction(resource) ? makeActionPath(resource) : joinPath(apiBase, resource, id);
|
|
65
78
|
validateResourceQuery(query, type);
|
|
66
79
|
|
|
67
80
|
if (Object.keys(params).length) {
|
|
@@ -1,5 +1,19 @@
|
|
|
1
|
+
import { RestAPILink } from '../RestAPILink';
|
|
1
2
|
import { queryToResourcePath } from './queryToResourcePath';
|
|
2
|
-
|
|
3
|
+
|
|
4
|
+
const createLink = config => new RestAPILink(config);
|
|
5
|
+
|
|
6
|
+
const defaultConfig = {
|
|
7
|
+
basePath: '<base>',
|
|
8
|
+
apiVersion: '37',
|
|
9
|
+
serverVersion: {
|
|
10
|
+
major: 2,
|
|
11
|
+
minor: 37,
|
|
12
|
+
patch: 11
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const link = createLink(defaultConfig);
|
|
16
|
+
const apiPath = link.versionedApiPath;
|
|
3
17
|
const actionPrefix = "dhis-web-commons/";
|
|
4
18
|
const actionPostfix = '.action';
|
|
5
19
|
describe('queryToResourcePath', () => {
|
|
@@ -8,7 +22,7 @@ describe('queryToResourcePath', () => {
|
|
|
8
22
|
const query = {
|
|
9
23
|
resource: 'action::test'
|
|
10
24
|
};
|
|
11
|
-
expect(queryToResourcePath(
|
|
25
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(actionPrefix, "test").concat(actionPostfix));
|
|
12
26
|
});
|
|
13
27
|
it('should return action URL with a simple querystring if query parameters are passed', () => {
|
|
14
28
|
const query = {
|
|
@@ -17,7 +31,7 @@ describe('queryToResourcePath', () => {
|
|
|
17
31
|
key: 'value'
|
|
18
32
|
}
|
|
19
33
|
};
|
|
20
|
-
expect(queryToResourcePath(
|
|
34
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(actionPrefix, "test").concat(actionPostfix, "?key=value"));
|
|
21
35
|
});
|
|
22
36
|
});
|
|
23
37
|
describe('resource with dot', () => {
|
|
@@ -25,14 +39,14 @@ describe('queryToResourcePath', () => {
|
|
|
25
39
|
const query = {
|
|
26
40
|
resource: 'svg.pdf'
|
|
27
41
|
};
|
|
28
|
-
expect(queryToResourcePath(
|
|
42
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/svg.pdf"));
|
|
29
43
|
});
|
|
30
44
|
});
|
|
31
45
|
it('should return resource url with no querystring if not query parameters are passed', () => {
|
|
32
46
|
const query = {
|
|
33
47
|
resource: 'test'
|
|
34
48
|
};
|
|
35
|
-
expect(queryToResourcePath(
|
|
49
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test"));
|
|
36
50
|
});
|
|
37
51
|
it('should return resource url and singular parameter separated by ?', () => {
|
|
38
52
|
const query = {
|
|
@@ -41,7 +55,7 @@ describe('queryToResourcePath', () => {
|
|
|
41
55
|
key: 'value'
|
|
42
56
|
}
|
|
43
57
|
};
|
|
44
|
-
expect(queryToResourcePath(
|
|
58
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value"));
|
|
45
59
|
});
|
|
46
60
|
it('should return resource url and multiple parameters separated by ? and &', () => {
|
|
47
61
|
const query = {
|
|
@@ -51,7 +65,7 @@ describe('queryToResourcePath', () => {
|
|
|
51
65
|
param: 'value2'
|
|
52
66
|
}
|
|
53
67
|
};
|
|
54
|
-
expect(queryToResourcePath(
|
|
68
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value¶m=value2"));
|
|
55
69
|
});
|
|
56
70
|
it('should url encode special characters in query keys', () => {
|
|
57
71
|
const query = {
|
|
@@ -60,7 +74,7 @@ describe('queryToResourcePath', () => {
|
|
|
60
74
|
'key=42&val': 'value'
|
|
61
75
|
}
|
|
62
76
|
};
|
|
63
|
-
expect(queryToResourcePath(
|
|
77
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key%3D42%26val=value"));
|
|
64
78
|
});
|
|
65
79
|
it('should url encode special characters in string parameters', () => {
|
|
66
80
|
const query = {
|
|
@@ -70,7 +84,7 @@ describe('queryToResourcePath', () => {
|
|
|
70
84
|
param: 'value2&& 53'
|
|
71
85
|
}
|
|
72
86
|
};
|
|
73
|
-
expect(queryToResourcePath(
|
|
87
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=value%3F%3D42¶m=value2%26%26%2053"));
|
|
74
88
|
});
|
|
75
89
|
it('should support numeric (integer and float) parameters', () => {
|
|
76
90
|
const query = {
|
|
@@ -80,7 +94,7 @@ describe('queryToResourcePath', () => {
|
|
|
80
94
|
param: 193.75
|
|
81
95
|
}
|
|
82
96
|
};
|
|
83
|
-
expect(queryToResourcePath(
|
|
97
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=42¶m=193.75"));
|
|
84
98
|
});
|
|
85
99
|
it('should support boolean parameters', () => {
|
|
86
100
|
const query = {
|
|
@@ -90,7 +104,7 @@ describe('queryToResourcePath', () => {
|
|
|
90
104
|
someflag: true
|
|
91
105
|
}
|
|
92
106
|
};
|
|
93
|
-
expect(queryToResourcePath(
|
|
107
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=42&someflag=true"));
|
|
94
108
|
});
|
|
95
109
|
it('should join array parameters with commas', () => {
|
|
96
110
|
const query = {
|
|
@@ -99,7 +113,7 @@ describe('queryToResourcePath', () => {
|
|
|
99
113
|
key: ['asdf', 123]
|
|
100
114
|
}
|
|
101
115
|
};
|
|
102
|
-
expect(queryToResourcePath(
|
|
116
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?key=asdf,123"));
|
|
103
117
|
});
|
|
104
118
|
it('should include multiple filter parameters when array of filters provided', () => {
|
|
105
119
|
const query = {
|
|
@@ -108,7 +122,7 @@ describe('queryToResourcePath', () => {
|
|
|
108
122
|
filter: ['asdf', 123]
|
|
109
123
|
}
|
|
110
124
|
};
|
|
111
|
-
expect(queryToResourcePath(
|
|
125
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(apiPath, "/test?filter=asdf&filter=123"));
|
|
112
126
|
});
|
|
113
127
|
it('should NOT YET support name-aliased parameters', () => {
|
|
114
128
|
const query = {
|
|
@@ -119,7 +133,7 @@ describe('queryToResourcePath', () => {
|
|
|
119
133
|
}
|
|
120
134
|
}
|
|
121
135
|
};
|
|
122
|
-
expect(() => queryToResourcePath(
|
|
136
|
+
expect(() => queryToResourcePath(link, query, 'read')).toThrow();
|
|
123
137
|
});
|
|
124
138
|
it('should throw if passed something crazy like a function', () => {
|
|
125
139
|
const query = {
|
|
@@ -128,6 +142,25 @@ describe('queryToResourcePath', () => {
|
|
|
128
142
|
key: a => a
|
|
129
143
|
}
|
|
130
144
|
};
|
|
131
|
-
expect(() => queryToResourcePath(
|
|
145
|
+
expect(() => queryToResourcePath(link, query, 'read')).toThrow();
|
|
146
|
+
});
|
|
147
|
+
it('should return an unversioned endpoint for the new tracker importer (in version 2.37)', () => {
|
|
148
|
+
const query = {
|
|
149
|
+
resource: 'tracker'
|
|
150
|
+
};
|
|
151
|
+
expect(queryToResourcePath(link, query, 'read')).toBe("".concat(link.unversionedApiPath, "/tracker"));
|
|
152
|
+
});
|
|
153
|
+
it('should return a VERSIONED endpoint for the new tracker importer (in version 2.38)', () => {
|
|
154
|
+
const query = {
|
|
155
|
+
resource: 'tracker'
|
|
156
|
+
};
|
|
157
|
+
const v38config = { ...defaultConfig,
|
|
158
|
+
serverVersion: {
|
|
159
|
+
major: 2,
|
|
160
|
+
minor: 38,
|
|
161
|
+
patch: 0
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
expect(queryToResourcePath(createLink(v38config), query, 'read')).toBe("".concat(link.versionedApiPath, "/tracker"));
|
|
132
165
|
});
|
|
133
166
|
});
|
|
@@ -5,29 +5,26 @@ import { joinPath } from './RestAPILink/path';
|
|
|
5
5
|
import { queryToRequestOptions } from './RestAPILink/queryToRequestOptions';
|
|
6
6
|
import { queryToResourcePath } from './RestAPILink/queryToResourcePath';
|
|
7
7
|
export class RestAPILink {
|
|
8
|
-
constructor({
|
|
9
|
-
|
|
10
|
-
apiVersion
|
|
11
|
-
}) {
|
|
12
|
-
_defineProperty(this, "apiPath", void 0);
|
|
8
|
+
constructor(config) {
|
|
9
|
+
_defineProperty(this, "config", void 0);
|
|
13
10
|
|
|
14
|
-
_defineProperty(this, "
|
|
11
|
+
_defineProperty(this, "versionedApiPath", void 0);
|
|
15
12
|
|
|
16
|
-
_defineProperty(this, "
|
|
13
|
+
_defineProperty(this, "unversionedApiPath", void 0);
|
|
17
14
|
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.versionedApiPath = joinPath('api', String(config.apiVersion));
|
|
17
|
+
this.unversionedApiPath = joinPath('api');
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
fetch(path, options) {
|
|
24
|
-
return fetchData(joinPath(this.baseUrl, path), options);
|
|
21
|
+
return fetchData(joinPath(this.config.baseUrl, path), options);
|
|
25
22
|
}
|
|
26
23
|
|
|
27
24
|
executeResourceQuery(type, query, {
|
|
28
25
|
signal
|
|
29
26
|
}) {
|
|
30
|
-
return this.fetch(queryToResourcePath(this
|
|
27
|
+
return this.fetch(queryToResourcePath(this, query, type), queryToRequestOptions(type, query, signal));
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FetchError } from './FetchError';
|
|
2
2
|
import { QueryVariables } from './Query';
|
|
3
|
-
export declare type FetchType = 'create' | 'read' | 'update' | 'replace' | 'delete';
|
|
3
|
+
export declare type FetchType = 'create' | 'read' | 'update' | 'json-patch' | 'replace' | 'delete';
|
|
4
4
|
export interface QueryExecuteOptions {
|
|
5
5
|
variables?: QueryVariables;
|
|
6
6
|
signal?: AbortSignal;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FetchError } from './FetchError';
|
|
2
2
|
import { ResourceQuery, QueryVariables } from './Query';
|
|
3
|
-
export declare type MutationType = 'create' | 'update' | 'replace' | 'delete';
|
|
3
|
+
export declare type MutationType = 'create' | 'update' | 'json-patch' | 'replace' | 'delete';
|
|
4
4
|
export interface MutationData {
|
|
5
5
|
[key: string]: any;
|
|
6
6
|
}
|
|
@@ -12,7 +12,7 @@ export interface CreateMutation extends BaseMutation {
|
|
|
12
12
|
data: MutationData;
|
|
13
13
|
}
|
|
14
14
|
export interface UpdateMutation extends BaseMutation {
|
|
15
|
-
type: 'update' | 'replace';
|
|
15
|
+
type: 'update' | 'replace' | 'json-patch';
|
|
16
16
|
id: string;
|
|
17
17
|
partial?: boolean;
|
|
18
18
|
data: MutationData;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ResolvedResourceQuery, FetchType } from '../../../engine';
|
|
2
|
+
export declare const isDataValue: (type: FetchType, { resource }: ResolvedResourceQuery) => boolean;
|
|
2
3
|
export declare const isFileResourceUpload: (type: FetchType, { resource }: ResolvedResourceQuery) => boolean;
|
|
3
4
|
export declare const isMessageConversationAttachment: (type: FetchType, { resource }: ResolvedResourceQuery) => boolean;
|
|
4
5
|
export declare const isStaticContentUpload: (type: FetchType, { resource }: ResolvedResourceQuery) => boolean;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { ResolvedResourceQuery, FetchType } from '../../../engine';
|
|
2
|
-
declare type RequestContentType = 'application/json' | 'text/plain' | 'multipart/form-data' | null;
|
|
2
|
+
declare type RequestContentType = 'application/json' | 'application/json-patch+json' | 'text/plain' | 'multipart/form-data' | null;
|
|
3
3
|
export declare const FORM_DATA_ERROR_MSG = "Could not convert data to FormData: object does not have own enumerable string-keyed properties";
|
|
4
|
-
export declare const requestContentType: (type: FetchType, query: ResolvedResourceQuery) =>
|
|
5
|
-
export declare const requestHeadersForContentType: (contentType: RequestContentType) =>
|
|
6
|
-
|
|
7
|
-
} | undefined;
|
|
8
|
-
export declare const requestBodyForContentType: (contentType: RequestContentType, { data }: ResolvedResourceQuery) => any;
|
|
4
|
+
export declare const requestContentType: (type: FetchType, query: ResolvedResourceQuery) => null | RequestContentType;
|
|
5
|
+
export declare const requestHeadersForContentType: (contentType: RequestContentType) => undefined | Record<'Content-Type', string>;
|
|
6
|
+
export declare const requestBodyForContentType: (contentType: RequestContentType, { data }: ResolvedResourceQuery) => undefined | string | FormData;
|
|
9
7
|
export {};
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { ResolvedResourceQuery, FetchType } from '../../engine';
|
|
2
|
-
|
|
2
|
+
import { RestAPILink } from '../RestAPILink';
|
|
3
|
+
export declare const queryToResourcePath: (link: RestAPILink, query: ResolvedResourceQuery, type: FetchType) => string;
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
+
import type { Config } from '@dhis2/app-service-config';
|
|
1
2
|
import { DataEngineLink, DataEngineLinkExecuteOptions, FetchType, JsonValue, ResolvedResourceQuery } from '../engine/';
|
|
2
|
-
export interface RestAPILinkInput {
|
|
3
|
-
baseUrl: string;
|
|
4
|
-
apiVersion: number;
|
|
5
|
-
}
|
|
6
3
|
export declare class RestAPILink implements DataEngineLink {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
constructor(
|
|
4
|
+
readonly config: Config;
|
|
5
|
+
readonly versionedApiPath: string;
|
|
6
|
+
readonly unversionedApiPath: string;
|
|
7
|
+
constructor(config: Config);
|
|
11
8
|
private fetch;
|
|
12
9
|
executeResourceQuery(type: FetchType, query: ResolvedResourceQuery, { signal }: DataEngineLinkExecuteOptions): Promise<JsonValue>;
|
|
13
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dhis2/app-service-data",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.1",
|
|
4
4
|
"main": "./build/cjs/index.js",
|
|
5
5
|
"module": "./build/es/index.js",
|
|
6
6
|
"types": "build/types/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"build/**"
|
|
23
23
|
],
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@dhis2/app-service-config": "3.
|
|
25
|
+
"@dhis2/app-service-config": "3.4.1",
|
|
26
26
|
"@dhis2/cli-app-scripts": "^7.1.1",
|
|
27
27
|
"prop-types": "^15.7.2",
|
|
28
28
|
"react": "^16.8",
|