@openfn/language-kobotoolbox 3.0.5 → 4.0.0
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/ast.json +48 -5
- package/dist/index.cjs +102 -65
- package/dist/index.js +102 -66
- package/package.json +7 -6
- package/types/Adaptor.d.ts +15 -30
- package/types/http.d.ts +47 -31
- package/types/util.d.ts +5 -0
- package/types/Utils.d.ts +0 -2
package/ast.json
CHANGED
|
@@ -43,16 +43,21 @@
|
|
|
43
43
|
"options"
|
|
44
44
|
],
|
|
45
45
|
"docs": {
|
|
46
|
-
"description": "Get submissions for a specific form. Calls `/api/v2/assets/<formId>/data
|
|
46
|
+
"description": "Get submissions for a specific form. Calls `/api/v2/assets/<formId>/data/`",
|
|
47
47
|
"tags": [
|
|
48
48
|
{
|
|
49
49
|
"title": "example",
|
|
50
|
-
"description": "getSubmissions('aXecHjmbATuF6iGFmvBLBX')
|
|
50
|
+
"description": "getSubmissions('aXecHjmbATuF6iGFmvBLBX');.",
|
|
51
|
+
"caption": "Get submissions for a specific form"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"title": "example",
|
|
55
|
+
"description": "getSubmissions('aXecHjmbATuF6iGFmvBLBX', { limit: Infinity });",
|
|
51
56
|
"caption": "Get all submissions for a specific form"
|
|
52
57
|
},
|
|
53
58
|
{
|
|
54
59
|
"title": "example",
|
|
55
|
-
"description": "getSubmissions('aXecHjmbATuF6iGFmvBLBX', { query: { _submission_time:{ $gte: \"
|
|
60
|
+
"description": "getSubmissions('aXecHjmbATuF6iGFmvBLBX', { query: { _submission_time:{ $gte: \"2025-03-12T21:54:20\" } } });",
|
|
56
61
|
"caption": "Get form submissions with a query"
|
|
57
62
|
},
|
|
58
63
|
{
|
|
@@ -76,7 +81,7 @@
|
|
|
76
81
|
},
|
|
77
82
|
{
|
|
78
83
|
"title": "param",
|
|
79
|
-
"description": "
|
|
84
|
+
"description": "Options to control the request",
|
|
80
85
|
"type": {
|
|
81
86
|
"type": "OptionalType",
|
|
82
87
|
"expression": {
|
|
@@ -87,6 +92,44 @@
|
|
|
87
92
|
"name": "options",
|
|
88
93
|
"default": "{}"
|
|
89
94
|
},
|
|
95
|
+
{
|
|
96
|
+
"title": "param",
|
|
97
|
+
"description": "Query options to filter the submissions. See query operators {@link http://docs.mongodb.org/manual/reference/operator/query/.}",
|
|
98
|
+
"type": {
|
|
99
|
+
"type": "OptionalType",
|
|
100
|
+
"expression": {
|
|
101
|
+
"type": "NameExpression",
|
|
102
|
+
"name": "object"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"name": "options.query"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"title": "param",
|
|
109
|
+
"description": "Maximum number of submissions to fetch. Pass Infinity to disable the limit and download all submissions",
|
|
110
|
+
"type": {
|
|
111
|
+
"type": "OptionalType",
|
|
112
|
+
"expression": {
|
|
113
|
+
"type": "NameExpression",
|
|
114
|
+
"name": "number"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"name": "options.limit",
|
|
118
|
+
"default": "30000"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"title": "param",
|
|
122
|
+
"description": "Limits the size of each page of submissions. Maximum value is 30000.",
|
|
123
|
+
"type": {
|
|
124
|
+
"type": "OptionalType",
|
|
125
|
+
"expression": {
|
|
126
|
+
"type": "NameExpression",
|
|
127
|
+
"name": "number"
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"name": "options.pageSize",
|
|
131
|
+
"default": "10000"
|
|
132
|
+
},
|
|
90
133
|
{
|
|
91
134
|
"title": "state",
|
|
92
135
|
"description": "data - an array of submission objects"
|
|
@@ -101,7 +144,7 @@
|
|
|
101
144
|
}
|
|
102
145
|
]
|
|
103
146
|
},
|
|
104
|
-
"valid":
|
|
147
|
+
"valid": false
|
|
105
148
|
},
|
|
106
149
|
{
|
|
107
150
|
"name": "getDeploymentInfo",
|
package/dist/index.cjs
CHANGED
|
@@ -58,7 +58,6 @@ __export(Adaptor_exports, {
|
|
|
58
58
|
getForms: () => getForms,
|
|
59
59
|
getSubmissions: () => getSubmissions,
|
|
60
60
|
group: () => import_language_common3.group,
|
|
61
|
-
http: () => import_language_common3.http,
|
|
62
61
|
lastReferenceValue: () => import_language_common3.lastReferenceValue,
|
|
63
62
|
merge: () => import_language_common3.merge,
|
|
64
63
|
sourceValue: () => import_language_common3.sourceValue
|
|
@@ -66,36 +65,29 @@ __export(Adaptor_exports, {
|
|
|
66
65
|
var import_language_common2 = require("@openfn/language-common");
|
|
67
66
|
var import_util2 = require("@openfn/language-common/util");
|
|
68
67
|
|
|
69
|
-
// src/
|
|
68
|
+
// src/util.js
|
|
70
69
|
var import_language_common = require("@openfn/language-common");
|
|
71
70
|
var import_util = require("@openfn/language-common/util");
|
|
72
|
-
var
|
|
71
|
+
var DEFAULT_LIMIT = 3e4;
|
|
72
|
+
var DEFAULT_PAGE_SIZE = 1e4;
|
|
73
|
+
function prepareNextState(state, response) {
|
|
73
74
|
const { body, ...responseWithoutBody } = response;
|
|
74
75
|
return {
|
|
75
|
-
...(0, import_language_common.composeNextState)(state,
|
|
76
|
+
...(0, import_language_common.composeNextState)(state, body),
|
|
76
77
|
response: responseWithoutBody
|
|
77
78
|
};
|
|
78
|
-
}
|
|
79
|
-
async function request(state, method, path, opts) {
|
|
80
|
-
const {
|
|
81
|
-
let baseUrl;
|
|
82
|
-
if (!state.configuration.baseUrl && baseURL) {
|
|
83
|
-
baseUrl = baseURL;
|
|
84
|
-
console.warn(
|
|
85
|
-
"No baseUrl found in state.configuration. baseURL will be used instead, but this will be deprecated in the future."
|
|
86
|
-
);
|
|
87
|
-
} else {
|
|
88
|
-
baseUrl = state.configuration.baseUrl;
|
|
89
|
-
}
|
|
79
|
+
}
|
|
80
|
+
async function request(state, method, path, opts = {}) {
|
|
81
|
+
const { baseUrl, apiVersion, username, password } = state.configuration;
|
|
90
82
|
const {
|
|
91
83
|
data = {},
|
|
92
84
|
query = {},
|
|
93
85
|
headers = {},
|
|
94
86
|
parseAs = "json",
|
|
95
|
-
|
|
87
|
+
maxRedirections
|
|
96
88
|
} = opts;
|
|
89
|
+
const requestPath = `/api/${apiVersion}/${path}/`;
|
|
97
90
|
const authHeaders = (0, import_util.makeBasicAuthHeader)(username, password);
|
|
98
|
-
let start, limit;
|
|
99
91
|
const options = {
|
|
100
92
|
body: data,
|
|
101
93
|
headers: {
|
|
@@ -107,34 +99,57 @@ async function request(state, method, path, opts) {
|
|
|
107
99
|
format: "json",
|
|
108
100
|
...query
|
|
109
101
|
},
|
|
102
|
+
maxRedirections,
|
|
110
103
|
parseAs,
|
|
111
|
-
baseUrl
|
|
104
|
+
baseUrl
|
|
112
105
|
};
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
106
|
+
return (0, import_util.request)(method, requestPath, options).then(import_util.logResponse);
|
|
107
|
+
}
|
|
108
|
+
async function requestWithPagination(state, path, options = {}) {
|
|
109
|
+
var _a, _b, _c, _d;
|
|
110
|
+
const results = [];
|
|
111
|
+
let { pageSize = DEFAULT_PAGE_SIZE, start, limit, ...otherOptions } = options;
|
|
112
|
+
const maxResults = limit ?? DEFAULT_LIMIT;
|
|
113
|
+
let isFirstRequest = true;
|
|
114
|
+
let requestOptions = { query: { start, limit: pageSize }, ...otherOptions };
|
|
115
|
+
let shouldFetchMoreContent = false;
|
|
116
|
+
const didUserPassLimit = Boolean(limit);
|
|
117
|
+
do {
|
|
118
|
+
requestOptions.query ?? (requestOptions.query = {});
|
|
119
|
+
if (start) {
|
|
120
|
+
requestOptions.query.start = start;
|
|
121
|
+
}
|
|
122
|
+
if (didUserPassLimit || !isFirstRequest) {
|
|
123
|
+
requestOptions.query.limit = Math.min(
|
|
124
|
+
pageSize || maxResults,
|
|
125
|
+
maxResults - results.length
|
|
118
126
|
);
|
|
127
|
+
} else if (!isNaN(pageSize)) {
|
|
128
|
+
requestOptions.query.limit = pageSize;
|
|
129
|
+
}
|
|
130
|
+
const response = await request(state, "GET", path, requestOptions);
|
|
131
|
+
if ((_a = response.body) == null ? void 0 : _a.results) {
|
|
119
132
|
results.push(...response.body.results);
|
|
120
|
-
if (response.body.next) {
|
|
121
|
-
const nextUrl = new URL(response.body.next);
|
|
122
|
-
|
|
123
|
-
start = Number(startDigit);
|
|
124
|
-
limit = nextUrl.searchParams.get("limit");
|
|
125
|
-
options.query = {
|
|
126
|
-
...options.query,
|
|
127
|
-
start,
|
|
128
|
-
limit
|
|
129
|
-
};
|
|
130
|
-
} else {
|
|
131
|
-
break;
|
|
133
|
+
if ((_b = response == null ? void 0 : response.body) == null ? void 0 : _b.next) {
|
|
134
|
+
const nextUrl = new URL((_c = response == null ? void 0 : response.body) == null ? void 0 : _c.next);
|
|
135
|
+
start = nextUrl.searchParams.get("start");
|
|
132
136
|
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
} else {
|
|
138
|
+
results.push(response.body);
|
|
139
|
+
}
|
|
140
|
+
if (isFirstRequest && !pageSize) {
|
|
141
|
+
pageSize = results.length;
|
|
142
|
+
}
|
|
143
|
+
isFirstRequest = false;
|
|
144
|
+
const hasMoreContent = (_d = response.body.next) == null ? void 0 : _d.includes("start=");
|
|
145
|
+
if (hasMoreContent && didUserPassLimit && results.length === maxResults) {
|
|
146
|
+
console.warn(
|
|
147
|
+
`Warning: The default maximum number of items has been reached (${maxResults}), but more items are available on the server. To download all available items, make another request with start=${maxResults + 1} or set max to Infinity`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
shouldFetchMoreContent = results.length < maxResults && hasMoreContent;
|
|
151
|
+
} while (shouldFetchMoreContent);
|
|
152
|
+
return results;
|
|
138
153
|
}
|
|
139
154
|
|
|
140
155
|
// src/Adaptor.js
|
|
@@ -153,34 +168,43 @@ function execute(...operations) {
|
|
|
153
168
|
}
|
|
154
169
|
function getForms() {
|
|
155
170
|
return async (state) => {
|
|
156
|
-
|
|
157
|
-
const response = await request(state, "GET",
|
|
158
|
-
|
|
171
|
+
var _a;
|
|
172
|
+
const response = await request(state, "GET", "assets", {
|
|
173
|
+
query: { asset_type: "survey" }
|
|
174
|
+
});
|
|
175
|
+
console.log("\u2713", (_a = response.body.results) == null ? void 0 : _a.length, "forms fetched.");
|
|
159
176
|
return prepareNextState(state, response);
|
|
160
177
|
};
|
|
161
178
|
}
|
|
162
|
-
function getSubmissions(formId, options
|
|
179
|
+
function getSubmissions(formId, options) {
|
|
163
180
|
return async (state) => {
|
|
164
|
-
const [resolvedFormId, resolvedOptions] = (0, import_util2.expandReferences)(
|
|
181
|
+
const [resolvedFormId, resolvedOptions = {}] = (0, import_util2.expandReferences)(
|
|
165
182
|
state,
|
|
166
183
|
formId,
|
|
167
184
|
options
|
|
168
185
|
);
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
186
|
+
const { query, limit, pageSize } = resolvedOptions;
|
|
187
|
+
const path = `/assets/${resolvedFormId}/data/`;
|
|
188
|
+
const qs = {};
|
|
189
|
+
if (query) {
|
|
190
|
+
if (typeof query === "string") {
|
|
191
|
+
qs.query = query;
|
|
174
192
|
} else {
|
|
175
|
-
|
|
193
|
+
qs.query = JSON.stringify(query);
|
|
176
194
|
}
|
|
177
195
|
}
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
196
|
+
const requestOptions = {
|
|
197
|
+
query: { ...qs },
|
|
198
|
+
limit,
|
|
199
|
+
pageSize
|
|
200
|
+
};
|
|
201
|
+
const result = await requestWithPagination(
|
|
202
|
+
state,
|
|
203
|
+
path,
|
|
204
|
+
requestOptions
|
|
205
|
+
);
|
|
206
|
+
console.log("\u2713", result == null ? void 0 : result.length, "submissions fetched.");
|
|
207
|
+
return (0, import_language_common2.composeNextState)(state, result);
|
|
184
208
|
};
|
|
185
209
|
}
|
|
186
210
|
function getDeploymentInfo(formId) {
|
|
@@ -198,12 +222,25 @@ var http_exports = {};
|
|
|
198
222
|
__export(http_exports, {
|
|
199
223
|
get: () => get,
|
|
200
224
|
post: () => post,
|
|
201
|
-
put: () => put
|
|
225
|
+
put: () => put,
|
|
226
|
+
request: () => request2
|
|
202
227
|
});
|
|
203
228
|
var import_util3 = require("@openfn/language-common/util");
|
|
204
|
-
function
|
|
229
|
+
function request2(method, path, options = {}) {
|
|
230
|
+
return async (state) => {
|
|
231
|
+
const [resolvedMethod, resolvedPath, resolvedOptions = {}] = (0, import_util3.expandReferences)(state, method, path, options);
|
|
232
|
+
const response = await request(
|
|
233
|
+
state,
|
|
234
|
+
resolvedMethod,
|
|
235
|
+
resolvedPath,
|
|
236
|
+
resolvedOptions
|
|
237
|
+
);
|
|
238
|
+
return prepareNextState(state, response);
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function get(path, options) {
|
|
205
242
|
return async (state) => {
|
|
206
|
-
const [resolvedPath, resolvedOptions] = (0, import_util3.expandReferences)(
|
|
243
|
+
const [resolvedPath, resolvedOptions = {}] = (0, import_util3.expandReferences)(
|
|
207
244
|
state,
|
|
208
245
|
path,
|
|
209
246
|
options
|
|
@@ -217,9 +254,9 @@ function get(path, options = {}) {
|
|
|
217
254
|
return prepareNextState(state, response);
|
|
218
255
|
};
|
|
219
256
|
}
|
|
220
|
-
function post(path, data, options
|
|
257
|
+
function post(path, data, options) {
|
|
221
258
|
return async (state) => {
|
|
222
|
-
const [resolvedPath, resolvedData, resolvedOptions] = (0, import_util3.expandReferences)(
|
|
259
|
+
const [resolvedPath, resolvedData, resolvedOptions = {}] = (0, import_util3.expandReferences)(
|
|
223
260
|
state,
|
|
224
261
|
path,
|
|
225
262
|
data,
|
|
@@ -238,9 +275,9 @@ function post(path, data, options = {}) {
|
|
|
238
275
|
return prepareNextState(state, response);
|
|
239
276
|
};
|
|
240
277
|
}
|
|
241
|
-
function put(path, data, options
|
|
278
|
+
function put(path, data, options) {
|
|
242
279
|
return async (state) => {
|
|
243
|
-
const [resolvedPath, resolvedData, resolvedOptions] = (0, import_util3.expandReferences)(
|
|
280
|
+
const [resolvedPath, resolvedData, resolvedOptions = {}] = (0, import_util3.expandReferences)(
|
|
244
281
|
state,
|
|
245
282
|
path,
|
|
246
283
|
data,
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,6 @@ __export(Adaptor_exports, {
|
|
|
21
21
|
getForms: () => getForms,
|
|
22
22
|
getSubmissions: () => getSubmissions,
|
|
23
23
|
group: () => group,
|
|
24
|
-
http: () => http,
|
|
25
24
|
lastReferenceValue: () => lastReferenceValue,
|
|
26
25
|
merge: () => merge,
|
|
27
26
|
sourceValue: () => sourceValue
|
|
@@ -32,40 +31,33 @@ import {
|
|
|
32
31
|
} from "@openfn/language-common";
|
|
33
32
|
import { expandReferences } from "@openfn/language-common/util";
|
|
34
33
|
|
|
35
|
-
// src/
|
|
34
|
+
// src/util.js
|
|
36
35
|
import { composeNextState } from "@openfn/language-common";
|
|
37
36
|
import {
|
|
38
37
|
request as commonRequest,
|
|
39
38
|
makeBasicAuthHeader,
|
|
40
39
|
logResponse
|
|
41
40
|
} from "@openfn/language-common/util";
|
|
42
|
-
var
|
|
41
|
+
var DEFAULT_LIMIT = 3e4;
|
|
42
|
+
var DEFAULT_PAGE_SIZE = 1e4;
|
|
43
|
+
function prepareNextState(state, response) {
|
|
43
44
|
const { body, ...responseWithoutBody } = response;
|
|
44
45
|
return {
|
|
45
|
-
...composeNextState(state,
|
|
46
|
+
...composeNextState(state, body),
|
|
46
47
|
response: responseWithoutBody
|
|
47
48
|
};
|
|
48
|
-
}
|
|
49
|
-
async function request(state, method, path, opts) {
|
|
50
|
-
const {
|
|
51
|
-
let baseUrl;
|
|
52
|
-
if (!state.configuration.baseUrl && baseURL) {
|
|
53
|
-
baseUrl = baseURL;
|
|
54
|
-
console.warn(
|
|
55
|
-
"No baseUrl found in state.configuration. baseURL will be used instead, but this will be deprecated in the future."
|
|
56
|
-
);
|
|
57
|
-
} else {
|
|
58
|
-
baseUrl = state.configuration.baseUrl;
|
|
59
|
-
}
|
|
49
|
+
}
|
|
50
|
+
async function request(state, method, path, opts = {}) {
|
|
51
|
+
const { baseUrl, apiVersion, username, password } = state.configuration;
|
|
60
52
|
const {
|
|
61
53
|
data = {},
|
|
62
54
|
query = {},
|
|
63
55
|
headers = {},
|
|
64
56
|
parseAs = "json",
|
|
65
|
-
|
|
57
|
+
maxRedirections
|
|
66
58
|
} = opts;
|
|
59
|
+
const requestPath = `/api/${apiVersion}/${path}/`;
|
|
67
60
|
const authHeaders = makeBasicAuthHeader(username, password);
|
|
68
|
-
let start, limit;
|
|
69
61
|
const options = {
|
|
70
62
|
body: data,
|
|
71
63
|
headers: {
|
|
@@ -77,34 +69,57 @@ async function request(state, method, path, opts) {
|
|
|
77
69
|
format: "json",
|
|
78
70
|
...query
|
|
79
71
|
},
|
|
72
|
+
maxRedirections,
|
|
80
73
|
parseAs,
|
|
81
|
-
baseUrl
|
|
74
|
+
baseUrl
|
|
82
75
|
};
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
76
|
+
return commonRequest(method, requestPath, options).then(logResponse);
|
|
77
|
+
}
|
|
78
|
+
async function requestWithPagination(state, path, options = {}) {
|
|
79
|
+
var _a, _b, _c, _d;
|
|
80
|
+
const results = [];
|
|
81
|
+
let { pageSize = DEFAULT_PAGE_SIZE, start, limit, ...otherOptions } = options;
|
|
82
|
+
const maxResults = limit ?? DEFAULT_LIMIT;
|
|
83
|
+
let isFirstRequest = true;
|
|
84
|
+
let requestOptions = { query: { start, limit: pageSize }, ...otherOptions };
|
|
85
|
+
let shouldFetchMoreContent = false;
|
|
86
|
+
const didUserPassLimit = Boolean(limit);
|
|
87
|
+
do {
|
|
88
|
+
requestOptions.query ?? (requestOptions.query = {});
|
|
89
|
+
if (start) {
|
|
90
|
+
requestOptions.query.start = start;
|
|
91
|
+
}
|
|
92
|
+
if (didUserPassLimit || !isFirstRequest) {
|
|
93
|
+
requestOptions.query.limit = Math.min(
|
|
94
|
+
pageSize || maxResults,
|
|
95
|
+
maxResults - results.length
|
|
88
96
|
);
|
|
97
|
+
} else if (!isNaN(pageSize)) {
|
|
98
|
+
requestOptions.query.limit = pageSize;
|
|
99
|
+
}
|
|
100
|
+
const response = await request(state, "GET", path, requestOptions);
|
|
101
|
+
if ((_a = response.body) == null ? void 0 : _a.results) {
|
|
89
102
|
results.push(...response.body.results);
|
|
90
|
-
if (response.body.next) {
|
|
91
|
-
const nextUrl = new URL(response.body.next);
|
|
92
|
-
|
|
93
|
-
start = Number(startDigit);
|
|
94
|
-
limit = nextUrl.searchParams.get("limit");
|
|
95
|
-
options.query = {
|
|
96
|
-
...options.query,
|
|
97
|
-
start,
|
|
98
|
-
limit
|
|
99
|
-
};
|
|
100
|
-
} else {
|
|
101
|
-
break;
|
|
103
|
+
if ((_b = response == null ? void 0 : response.body) == null ? void 0 : _b.next) {
|
|
104
|
+
const nextUrl = new URL((_c = response == null ? void 0 : response.body) == null ? void 0 : _c.next);
|
|
105
|
+
start = nextUrl.searchParams.get("start");
|
|
102
106
|
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
} else {
|
|
108
|
+
results.push(response.body);
|
|
109
|
+
}
|
|
110
|
+
if (isFirstRequest && !pageSize) {
|
|
111
|
+
pageSize = results.length;
|
|
112
|
+
}
|
|
113
|
+
isFirstRequest = false;
|
|
114
|
+
const hasMoreContent = (_d = response.body.next) == null ? void 0 : _d.includes("start=");
|
|
115
|
+
if (hasMoreContent && didUserPassLimit && results.length === maxResults) {
|
|
116
|
+
console.warn(
|
|
117
|
+
`Warning: The default maximum number of items has been reached (${maxResults}), but more items are available on the server. To download all available items, make another request with start=${maxResults + 1} or set max to Infinity`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
shouldFetchMoreContent = results.length < maxResults && hasMoreContent;
|
|
121
|
+
} while (shouldFetchMoreContent);
|
|
122
|
+
return results;
|
|
108
123
|
}
|
|
109
124
|
|
|
110
125
|
// src/Adaptor.js
|
|
@@ -118,7 +133,6 @@ import {
|
|
|
118
133
|
fields,
|
|
119
134
|
fn,
|
|
120
135
|
fnIf,
|
|
121
|
-
http,
|
|
122
136
|
group,
|
|
123
137
|
lastReferenceValue,
|
|
124
138
|
merge,
|
|
@@ -138,34 +152,43 @@ function execute(...operations) {
|
|
|
138
152
|
}
|
|
139
153
|
function getForms() {
|
|
140
154
|
return async (state) => {
|
|
141
|
-
|
|
142
|
-
const response = await request(state, "GET",
|
|
143
|
-
|
|
155
|
+
var _a;
|
|
156
|
+
const response = await request(state, "GET", "assets", {
|
|
157
|
+
query: { asset_type: "survey" }
|
|
158
|
+
});
|
|
159
|
+
console.log("\u2713", (_a = response.body.results) == null ? void 0 : _a.length, "forms fetched.");
|
|
144
160
|
return prepareNextState(state, response);
|
|
145
161
|
};
|
|
146
162
|
}
|
|
147
|
-
function getSubmissions(formId, options
|
|
163
|
+
function getSubmissions(formId, options) {
|
|
148
164
|
return async (state) => {
|
|
149
|
-
const [resolvedFormId, resolvedOptions] = expandReferences(
|
|
165
|
+
const [resolvedFormId, resolvedOptions = {}] = expandReferences(
|
|
150
166
|
state,
|
|
151
167
|
formId,
|
|
152
168
|
options
|
|
153
169
|
);
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
170
|
+
const { query, limit, pageSize } = resolvedOptions;
|
|
171
|
+
const path = `/assets/${resolvedFormId}/data/`;
|
|
172
|
+
const qs = {};
|
|
173
|
+
if (query) {
|
|
174
|
+
if (typeof query === "string") {
|
|
175
|
+
qs.query = query;
|
|
159
176
|
} else {
|
|
160
|
-
|
|
177
|
+
qs.query = JSON.stringify(query);
|
|
161
178
|
}
|
|
162
179
|
}
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
180
|
+
const requestOptions = {
|
|
181
|
+
query: { ...qs },
|
|
182
|
+
limit,
|
|
183
|
+
pageSize
|
|
184
|
+
};
|
|
185
|
+
const result = await requestWithPagination(
|
|
186
|
+
state,
|
|
187
|
+
path,
|
|
188
|
+
requestOptions
|
|
189
|
+
);
|
|
190
|
+
console.log("\u2713", result == null ? void 0 : result.length, "submissions fetched.");
|
|
191
|
+
return composeNextState2(state, result);
|
|
169
192
|
};
|
|
170
193
|
}
|
|
171
194
|
function getDeploymentInfo(formId) {
|
|
@@ -183,12 +206,25 @@ var http_exports = {};
|
|
|
183
206
|
__export(http_exports, {
|
|
184
207
|
get: () => get,
|
|
185
208
|
post: () => post,
|
|
186
|
-
put: () => put
|
|
209
|
+
put: () => put,
|
|
210
|
+
request: () => request2
|
|
187
211
|
});
|
|
188
212
|
import { expandReferences as expandReferences2 } from "@openfn/language-common/util";
|
|
189
|
-
function
|
|
213
|
+
function request2(method, path, options = {}) {
|
|
214
|
+
return async (state) => {
|
|
215
|
+
const [resolvedMethod, resolvedPath, resolvedOptions = {}] = expandReferences2(state, method, path, options);
|
|
216
|
+
const response = await request(
|
|
217
|
+
state,
|
|
218
|
+
resolvedMethod,
|
|
219
|
+
resolvedPath,
|
|
220
|
+
resolvedOptions
|
|
221
|
+
);
|
|
222
|
+
return prepareNextState(state, response);
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function get(path, options) {
|
|
190
226
|
return async (state) => {
|
|
191
|
-
const [resolvedPath, resolvedOptions] = expandReferences2(
|
|
227
|
+
const [resolvedPath, resolvedOptions = {}] = expandReferences2(
|
|
192
228
|
state,
|
|
193
229
|
path,
|
|
194
230
|
options
|
|
@@ -202,9 +238,9 @@ function get(path, options = {}) {
|
|
|
202
238
|
return prepareNextState(state, response);
|
|
203
239
|
};
|
|
204
240
|
}
|
|
205
|
-
function post(path, data, options
|
|
241
|
+
function post(path, data, options) {
|
|
206
242
|
return async (state) => {
|
|
207
|
-
const [resolvedPath, resolvedData, resolvedOptions] = expandReferences2(
|
|
243
|
+
const [resolvedPath, resolvedData, resolvedOptions = {}] = expandReferences2(
|
|
208
244
|
state,
|
|
209
245
|
path,
|
|
210
246
|
data,
|
|
@@ -223,9 +259,9 @@ function post(path, data, options = {}) {
|
|
|
223
259
|
return prepareNextState(state, response);
|
|
224
260
|
};
|
|
225
261
|
}
|
|
226
|
-
function put(path, data, options
|
|
262
|
+
function put(path, data, options) {
|
|
227
263
|
return async (state) => {
|
|
228
|
-
const [resolvedPath, resolvedData, resolvedOptions] = expandReferences2(
|
|
264
|
+
const [resolvedPath, resolvedData, resolvedOptions = {}] = expandReferences2(
|
|
229
265
|
state,
|
|
230
266
|
path,
|
|
231
267
|
data,
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfn/language-kobotoolbox",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"label": "KoboToolbox",
|
|
4
|
+
"version": "4.0.0",
|
|
5
|
+
"description": "A KoboToolbox Language Pack for OpenFn",
|
|
5
6
|
"homepage": "https://docs.openfn.org",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
@@ -21,9 +22,8 @@
|
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
24
|
"assertion-error": "^1.0.1",
|
|
24
|
-
"chai": "^
|
|
25
|
+
"chai": "^5.2.0",
|
|
25
26
|
"deep-eql": "^0.1.3",
|
|
26
|
-
"esno": "^0.16.3",
|
|
27
27
|
"rimraf": "^3.0.2"
|
|
28
28
|
},
|
|
29
29
|
"type": "module",
|
|
@@ -38,8 +38,9 @@
|
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "pnpm clean && build-adaptor kobotoolbox",
|
|
41
|
-
"test": "mocha --experimental-specifier-resolution=node --no-warnings",
|
|
42
|
-
"test:watch": "mocha -w --experimental-specifier-resolution=node --no-warnings",
|
|
41
|
+
"test": "mocha --experimental-specifier-resolution=node --no-warnings --exclude test/integration.js --recursive",
|
|
42
|
+
"test:watch": "mocha -w --experimental-specifier-resolution=node --no-warnings --exclude test/integration.js --recursive",
|
|
43
|
+
"test:integration": "mocha --experimental-specifier-resolution=node --no-warnings test/integration.js",
|
|
43
44
|
"clean": "rimraf dist types docs",
|
|
44
45
|
"pack": "pnpm pack --pack-destination ../../dist",
|
|
45
46
|
"lint": "eslint src"
|
package/types/Adaptor.d.ts
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Options object
|
|
3
|
-
* @typedef {Object} RequestOptions
|
|
4
|
-
* @property {object} query - An object of query parameters to be encoded into the URL
|
|
5
|
-
* @property {object} headers - An object of all request headers
|
|
6
|
-
* @property {string} [parseAs='json'] - The response format to parse (e.g., 'json', 'text', or 'stream')
|
|
7
|
-
*/
|
|
8
1
|
/**
|
|
9
2
|
* Execute a sequence of operations.
|
|
10
3
|
* Wraps `language-common/execute`, and prepends initial state for http.
|
|
@@ -29,19 +22,28 @@ export function execute(...operations: Operations): Operation;
|
|
|
29
22
|
*/
|
|
30
23
|
export function getForms(): Operation;
|
|
31
24
|
/**
|
|
32
|
-
* Get submissions for a specific form. Calls `/api/v2/assets/<formId>/data
|
|
25
|
+
* Get submissions for a specific form. Calls `/api/v2/assets/<formId>/data/`
|
|
26
|
+
* @example <caption>Get submissions for a specific form</caption>
|
|
27
|
+
* getSubmissions('aXecHjmbATuF6iGFmvBLBX');.
|
|
33
28
|
* @example <caption>Get all submissions for a specific form</caption>
|
|
34
|
-
* getSubmissions('aXecHjmbATuF6iGFmvBLBX');
|
|
29
|
+
* getSubmissions('aXecHjmbATuF6iGFmvBLBX', { limit: Infinity });
|
|
35
30
|
* @example <caption>Get form submissions with a query</caption>
|
|
36
|
-
* getSubmissions('aXecHjmbATuF6iGFmvBLBX', { query: { _submission_time:{ $gte: "
|
|
31
|
+
* getSubmissions('aXecHjmbATuF6iGFmvBLBX', { query: { _submission_time:{ $gte: "2025-03-12T21:54:20" } } });
|
|
37
32
|
* @function
|
|
38
33
|
* @public
|
|
39
34
|
* @param {string} formId - Form Id to get the specific submissions
|
|
40
|
-
* @param {object} [options={}] -
|
|
35
|
+
* @param {object} [options={}] - Options to control the request
|
|
36
|
+
* @param {object} [options.query] - Query options to filter the submissions. See query operators {@link http://docs.mongodb.org/manual/reference/operator/query/.}
|
|
37
|
+
* @param {number} [options.limit=30000] - Maximum number of submissions to fetch. Pass Infinity to disable the limit and download all submissions
|
|
38
|
+
* @param {number} [options.pageSize=10000] - Limits the size of each page of submissions. Maximum value is 30000.
|
|
41
39
|
* @state data - an array of submission objects
|
|
42
40
|
* @returns {Operation}
|
|
43
41
|
*/
|
|
44
|
-
export function getSubmissions(formId: string, options?:
|
|
42
|
+
export function getSubmissions(formId: string, options?: {
|
|
43
|
+
query?: object;
|
|
44
|
+
limit?: number;
|
|
45
|
+
pageSize?: number;
|
|
46
|
+
}): Operation;
|
|
45
47
|
/**
|
|
46
48
|
* Get deployment information for a specific form. Calls `/api/v2/assets/<id>/deployment/`.
|
|
47
49
|
* @example
|
|
@@ -53,21 +55,4 @@ export function getSubmissions(formId: string, options?: object): Operation;
|
|
|
53
55
|
* @returns {Operation}
|
|
54
56
|
*/
|
|
55
57
|
export function getDeploymentInfo(formId: string): Operation;
|
|
56
|
-
|
|
57
|
-
* Options object
|
|
58
|
-
*/
|
|
59
|
-
export type RequestOptions = {
|
|
60
|
-
/**
|
|
61
|
-
* - An object of query parameters to be encoded into the URL
|
|
62
|
-
*/
|
|
63
|
-
query: object;
|
|
64
|
-
/**
|
|
65
|
-
* - An object of all request headers
|
|
66
|
-
*/
|
|
67
|
-
headers: object;
|
|
68
|
-
/**
|
|
69
|
-
* - The response format to parse (e.g., 'json', 'text', or 'stream')
|
|
70
|
-
*/
|
|
71
|
-
parseAs?: string;
|
|
72
|
-
};
|
|
73
|
-
export { alterState, cursor, dataPath, dataValue, each, field, fields, fn, fnIf, http, group, lastReferenceValue, merge, sourceValue } from "@openfn/language-common";
|
|
58
|
+
export { alterState, cursor, dataPath, dataValue, each, field, fields, fn, fnIf, group, lastReferenceValue, merge, sourceValue } from "@openfn/language-common";
|
package/types/http.d.ts
CHANGED
|
@@ -1,31 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* State object
|
|
3
|
-
* @typedef {Object}
|
|
3
|
+
* @typedef {Object} HttpState
|
|
4
|
+
* @private
|
|
4
5
|
* @property data - The response body (as JSON)
|
|
5
|
-
* @property response - The HTTP response from the KoboToolbox server (excluding the body)
|
|
6
|
-
* @property references - An array
|
|
6
|
+
* @property response - The HTTP response from the KoboToolbox server (excluding the body)
|
|
7
|
+
* @property references - An array containing all previous data objects
|
|
7
8
|
*/
|
|
8
9
|
/**
|
|
9
10
|
* Options object
|
|
10
|
-
* @typedef {Object}
|
|
11
|
+
* @typedef {Object} HTTPRequestOptions
|
|
11
12
|
* @property {object} query - An object of query parameters to be encoded into the URL
|
|
12
13
|
* @property {object} headers - An object of all request headers
|
|
14
|
+
* @property {object} body - The request body (as JSON)
|
|
15
|
+
* @property {number} maxRedirections - The maximum number of redirects to follow
|
|
13
16
|
* @property {string} [parseAs='json'] - The response format to parse (e.g., 'json', 'text', or 'stream')
|
|
14
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* Make a HTTP request to any KoboToolbox endpoint
|
|
20
|
+
* @example <caption>Bulk updating of submissions</caption>
|
|
21
|
+
* http.request("PATCH", `assets/${$.form_uid}/data/bulk/`, {
|
|
22
|
+
* body: {
|
|
23
|
+
* submission_ids: [$.data.submission_id],
|
|
24
|
+
* data: {
|
|
25
|
+
* Transaction_status: "success",
|
|
26
|
+
* },
|
|
27
|
+
* },
|
|
28
|
+
* });
|
|
29
|
+
* @function
|
|
30
|
+
* @public
|
|
31
|
+
* @param {string} method - HTTP method to use
|
|
32
|
+
* @param {string} path - Path to resource
|
|
33
|
+
* @param {HTTPRequestOptions} [options={}] - An object containing query, headers, and body for the request
|
|
34
|
+
* @state {HttpState}
|
|
35
|
+
* @returns {Operation}
|
|
36
|
+
*/
|
|
37
|
+
export function request(method: string, path: string, options?: HTTPRequestOptions): Operation;
|
|
15
38
|
/**
|
|
16
39
|
* Make a GET request to any KoboToolbox endpoint.
|
|
17
40
|
* @public
|
|
18
41
|
* @function
|
|
19
42
|
* @example <caption>GET assets resource</caption>
|
|
20
|
-
* http.get(
|
|
21
|
-
* "/assets/",
|
|
22
|
-
* )
|
|
43
|
+
* http.get('assets')
|
|
23
44
|
* @param {string} path - path to resource
|
|
24
|
-
* @param {
|
|
25
|
-
* @state {
|
|
45
|
+
* @param {HTTPRequestOptions} [options={}] - An object containing query params and headers for the request
|
|
46
|
+
* @state {HttpState}
|
|
26
47
|
* @returns {operation}
|
|
27
48
|
*/
|
|
28
|
-
export function get(path: string, options?:
|
|
49
|
+
export function get(path: string, options?: HTTPRequestOptions): operation;
|
|
29
50
|
/**
|
|
30
51
|
* Make a POST request to a KoboToolbox endpoint
|
|
31
52
|
* @public
|
|
@@ -40,11 +61,11 @@ export function get(path: string, options?: RequestOptions): operation;
|
|
|
40
61
|
* );
|
|
41
62
|
* @param {string} path - path to resource
|
|
42
63
|
* @param {any} data - the body data in JSON format
|
|
43
|
-
* @param {
|
|
44
|
-
* @state {
|
|
64
|
+
* @param {HTTPRequestOptions} [options={}] - An object containing query params and headers for the request
|
|
65
|
+
* @state {HttpState}
|
|
45
66
|
* @returns {operation}
|
|
46
67
|
*/
|
|
47
|
-
export function post(path: string, data: any, options?:
|
|
68
|
+
export function post(path: string, data: any, options?: HTTPRequestOptions): operation;
|
|
48
69
|
/**
|
|
49
70
|
* Make a PUT request to a KoboToolbox endpoint
|
|
50
71
|
* @public
|
|
@@ -59,32 +80,19 @@ export function post(path: string, data: any, options?: RequestOptions): operati
|
|
|
59
80
|
* );
|
|
60
81
|
* @param {string} path - path to resource
|
|
61
82
|
* @param {any} data - the body data in JSON format
|
|
62
|
-
* @param {
|
|
63
|
-
* @state {
|
|
83
|
+
* @param {HTTPRequestOptions} [options={}] - An object containing query params and headers for the request
|
|
84
|
+
* @state {HttpState}
|
|
64
85
|
* @returns {operation}
|
|
65
86
|
*/
|
|
66
|
-
export function put(path: string, data: any, options?:
|
|
87
|
+
export function put(path: string, data: any, options?: HTTPRequestOptions): operation;
|
|
67
88
|
/**
|
|
68
89
|
* State object
|
|
69
90
|
*/
|
|
70
|
-
export type
|
|
71
|
-
/**
|
|
72
|
-
* - The response body (as JSON)
|
|
73
|
-
*/
|
|
74
|
-
data: any;
|
|
75
|
-
/**
|
|
76
|
-
* - The HTTP response from the KoboToolbox server (excluding the body). Responses will be returned in JSON format
|
|
77
|
-
*/
|
|
78
|
-
response: any;
|
|
79
|
-
/**
|
|
80
|
-
* - An array of all previous data objects used in the Job
|
|
81
|
-
*/
|
|
82
|
-
references: any;
|
|
83
|
-
};
|
|
91
|
+
export type HttpState = any;
|
|
84
92
|
/**
|
|
85
93
|
* Options object
|
|
86
94
|
*/
|
|
87
|
-
export type
|
|
95
|
+
export type HTTPRequestOptions = {
|
|
88
96
|
/**
|
|
89
97
|
* - An object of query parameters to be encoded into the URL
|
|
90
98
|
*/
|
|
@@ -93,6 +101,14 @@ export type RequestOptions = {
|
|
|
93
101
|
* - An object of all request headers
|
|
94
102
|
*/
|
|
95
103
|
headers: object;
|
|
104
|
+
/**
|
|
105
|
+
* - The request body (as JSON)
|
|
106
|
+
*/
|
|
107
|
+
body: object;
|
|
108
|
+
/**
|
|
109
|
+
* - The maximum number of redirects to follow
|
|
110
|
+
*/
|
|
111
|
+
maxRedirections: number;
|
|
96
112
|
/**
|
|
97
113
|
* - The response format to parse (e.g., 'json', 'text', or 'stream')
|
|
98
114
|
*/
|
package/types/util.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export function prepareNextState(state: any, response: any): any;
|
|
2
|
+
export function request(state: any, method: any, path: any, opts?: {}): Promise<any>;
|
|
3
|
+
export function requestWithPagination(state: any, path: any, options?: {}): Promise<any[]>;
|
|
4
|
+
export const DEFAULT_LIMIT: 30000;
|
|
5
|
+
export const DEFAULT_PAGE_SIZE: 10000;
|
package/types/Utils.d.ts
DELETED