@openfn/language-kobotoolbox 3.0.5 → 4.1.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 +83 -5
- package/dist/index.cjs +107 -67
- package/dist/index.js +107 -68
- package/package.json +7 -6
- package/types/Adaptor.d.ts +23 -30
- package/types/http.d.ts +47 -31
- package/types/util.d.ts +6 -0
- package/types/Utils.d.ts +0 -2
package/ast.json
CHANGED
|
@@ -43,18 +43,33 @@
|
|
|
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
|
},
|
|
63
|
+
{
|
|
64
|
+
"title": "example",
|
|
65
|
+
"description": "getSubmissions('aXecHjmbATuF6iGFmvBLBX', { sort: { _submission_time: -1 } });",
|
|
66
|
+
"caption": "Get form submissions with sorting"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"title": "example",
|
|
70
|
+
"description": "getSubmissions('aXecHjmbATuF6iGFmvBLBX', { start: 10 });",
|
|
71
|
+
"caption": "Get form submissions with specific start index"
|
|
72
|
+
},
|
|
58
73
|
{
|
|
59
74
|
"title": "function",
|
|
60
75
|
"description": null,
|
|
@@ -76,7 +91,7 @@
|
|
|
76
91
|
},
|
|
77
92
|
{
|
|
78
93
|
"title": "param",
|
|
79
|
-
"description": "
|
|
94
|
+
"description": "Options to control the request",
|
|
80
95
|
"type": {
|
|
81
96
|
"type": "OptionalType",
|
|
82
97
|
"expression": {
|
|
@@ -87,6 +102,69 @@
|
|
|
87
102
|
"name": "options",
|
|
88
103
|
"default": "{}"
|
|
89
104
|
},
|
|
105
|
+
{
|
|
106
|
+
"title": "param",
|
|
107
|
+
"description": "Field and direction to sort submissions by.",
|
|
108
|
+
"type": {
|
|
109
|
+
"type": "OptionalType",
|
|
110
|
+
"expression": {
|
|
111
|
+
"type": "NameExpression",
|
|
112
|
+
"name": "object"
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"name": "options.sort"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"title": "param",
|
|
119
|
+
"description": "Query options to filter the submissions. See query operators {@link http://docs.mongodb.org/manual/reference/operator/query/.}",
|
|
120
|
+
"type": {
|
|
121
|
+
"type": "OptionalType",
|
|
122
|
+
"expression": {
|
|
123
|
+
"type": "NameExpression",
|
|
124
|
+
"name": "object"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"name": "options.query"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"title": "param",
|
|
131
|
+
"description": "The index of the first submission to return.",
|
|
132
|
+
"type": {
|
|
133
|
+
"type": "OptionalType",
|
|
134
|
+
"expression": {
|
|
135
|
+
"type": "NameExpression",
|
|
136
|
+
"name": "number"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"name": "options.start",
|
|
140
|
+
"default": "0"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"title": "param",
|
|
144
|
+
"description": "Maximum number of submissions to fetch. Pass Infinity to disable the limit and download all submissions",
|
|
145
|
+
"type": {
|
|
146
|
+
"type": "OptionalType",
|
|
147
|
+
"expression": {
|
|
148
|
+
"type": "NameExpression",
|
|
149
|
+
"name": "number"
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
"name": "options.limit",
|
|
153
|
+
"default": "30000"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"title": "param",
|
|
157
|
+
"description": "Limits the size of each page of submissions. Maximum value is 30000.",
|
|
158
|
+
"type": {
|
|
159
|
+
"type": "OptionalType",
|
|
160
|
+
"expression": {
|
|
161
|
+
"type": "NameExpression",
|
|
162
|
+
"name": "number"
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"name": "options.pageSize",
|
|
166
|
+
"default": "10000"
|
|
167
|
+
},
|
|
90
168
|
{
|
|
91
169
|
"title": "state",
|
|
92
170
|
"description": "data - an array of submission objects"
|
|
@@ -101,7 +179,7 @@
|
|
|
101
179
|
}
|
|
102
180
|
]
|
|
103
181
|
},
|
|
104
|
-
"valid":
|
|
182
|
+
"valid": false
|
|
105
183
|
},
|
|
106
184
|
{
|
|
107
185
|
"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,60 @@ 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;
|
|
153
|
+
}
|
|
154
|
+
function maybeStringify(query) {
|
|
155
|
+
return typeof query === "string" ? query : JSON.stringify(query);
|
|
138
156
|
}
|
|
139
157
|
|
|
140
158
|
// src/Adaptor.js
|
|
@@ -153,34 +171,43 @@ function execute(...operations) {
|
|
|
153
171
|
}
|
|
154
172
|
function getForms() {
|
|
155
173
|
return async (state) => {
|
|
156
|
-
|
|
157
|
-
const response = await request(state, "GET",
|
|
158
|
-
|
|
174
|
+
var _a;
|
|
175
|
+
const response = await request(state, "GET", "assets", {
|
|
176
|
+
query: { asset_type: "survey" }
|
|
177
|
+
});
|
|
178
|
+
console.log("\u2713", (_a = response.body.results) == null ? void 0 : _a.length, "forms fetched.");
|
|
159
179
|
return prepareNextState(state, response);
|
|
160
180
|
};
|
|
161
181
|
}
|
|
162
|
-
function getSubmissions(formId, options
|
|
182
|
+
function getSubmissions(formId, options) {
|
|
163
183
|
return async (state) => {
|
|
164
|
-
const [resolvedFormId, resolvedOptions] = (0, import_util2.expandReferences)(
|
|
184
|
+
const [resolvedFormId, resolvedOptions = {}] = (0, import_util2.expandReferences)(
|
|
165
185
|
state,
|
|
166
186
|
formId,
|
|
167
187
|
options
|
|
168
188
|
);
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
} else {
|
|
175
|
-
query.query = JSON.stringify(resolvedOptions.query);
|
|
176
|
-
}
|
|
189
|
+
const { query, limit, pageSize, sort, start } = resolvedOptions;
|
|
190
|
+
const path = `/assets/${resolvedFormId}/data/`;
|
|
191
|
+
const qs = {};
|
|
192
|
+
if (query) {
|
|
193
|
+
qs.query = maybeStringify(query);
|
|
177
194
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
195
|
+
if (sort) {
|
|
196
|
+
qs.sort = maybeStringify(sort);
|
|
197
|
+
}
|
|
198
|
+
const requestOptions = {
|
|
199
|
+
query: { ...qs },
|
|
200
|
+
start,
|
|
201
|
+
limit,
|
|
202
|
+
pageSize
|
|
203
|
+
};
|
|
204
|
+
const result = await requestWithPagination(
|
|
205
|
+
state,
|
|
206
|
+
path,
|
|
207
|
+
requestOptions
|
|
208
|
+
);
|
|
209
|
+
console.log("\u2713", result == null ? void 0 : result.length, "submissions fetched.");
|
|
210
|
+
return (0, import_language_common2.composeNextState)(state, result);
|
|
184
211
|
};
|
|
185
212
|
}
|
|
186
213
|
function getDeploymentInfo(formId) {
|
|
@@ -198,12 +225,25 @@ var http_exports = {};
|
|
|
198
225
|
__export(http_exports, {
|
|
199
226
|
get: () => get,
|
|
200
227
|
post: () => post,
|
|
201
|
-
put: () => put
|
|
228
|
+
put: () => put,
|
|
229
|
+
request: () => request2
|
|
202
230
|
});
|
|
203
231
|
var import_util3 = require("@openfn/language-common/util");
|
|
204
|
-
function
|
|
232
|
+
function request2(method, path, options = {}) {
|
|
233
|
+
return async (state) => {
|
|
234
|
+
const [resolvedMethod, resolvedPath, resolvedOptions = {}] = (0, import_util3.expandReferences)(state, method, path, options);
|
|
235
|
+
const response = await request(
|
|
236
|
+
state,
|
|
237
|
+
resolvedMethod,
|
|
238
|
+
resolvedPath,
|
|
239
|
+
resolvedOptions
|
|
240
|
+
);
|
|
241
|
+
return prepareNextState(state, response);
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function get(path, options) {
|
|
205
245
|
return async (state) => {
|
|
206
|
-
const [resolvedPath, resolvedOptions] = (0, import_util3.expandReferences)(
|
|
246
|
+
const [resolvedPath, resolvedOptions = {}] = (0, import_util3.expandReferences)(
|
|
207
247
|
state,
|
|
208
248
|
path,
|
|
209
249
|
options
|
|
@@ -217,9 +257,9 @@ function get(path, options = {}) {
|
|
|
217
257
|
return prepareNextState(state, response);
|
|
218
258
|
};
|
|
219
259
|
}
|
|
220
|
-
function post(path, data, options
|
|
260
|
+
function post(path, data, options) {
|
|
221
261
|
return async (state) => {
|
|
222
|
-
const [resolvedPath, resolvedData, resolvedOptions] = (0, import_util3.expandReferences)(
|
|
262
|
+
const [resolvedPath, resolvedData, resolvedOptions = {}] = (0, import_util3.expandReferences)(
|
|
223
263
|
state,
|
|
224
264
|
path,
|
|
225
265
|
data,
|
|
@@ -238,9 +278,9 @@ function post(path, data, options = {}) {
|
|
|
238
278
|
return prepareNextState(state, response);
|
|
239
279
|
};
|
|
240
280
|
}
|
|
241
|
-
function put(path, data, options
|
|
281
|
+
function put(path, data, options) {
|
|
242
282
|
return async (state) => {
|
|
243
|
-
const [resolvedPath, resolvedData, resolvedOptions] = (0, import_util3.expandReferences)(
|
|
283
|
+
const [resolvedPath, resolvedData, resolvedOptions = {}] = (0, import_util3.expandReferences)(
|
|
244
284
|
state,
|
|
245
285
|
path,
|
|
246
286
|
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,60 @@ 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;
|
|
123
|
+
}
|
|
124
|
+
function maybeStringify(query) {
|
|
125
|
+
return typeof query === "string" ? query : JSON.stringify(query);
|
|
108
126
|
}
|
|
109
127
|
|
|
110
128
|
// src/Adaptor.js
|
|
@@ -118,7 +136,6 @@ import {
|
|
|
118
136
|
fields,
|
|
119
137
|
fn,
|
|
120
138
|
fnIf,
|
|
121
|
-
http,
|
|
122
139
|
group,
|
|
123
140
|
lastReferenceValue,
|
|
124
141
|
merge,
|
|
@@ -138,34 +155,43 @@ function execute(...operations) {
|
|
|
138
155
|
}
|
|
139
156
|
function getForms() {
|
|
140
157
|
return async (state) => {
|
|
141
|
-
|
|
142
|
-
const response = await request(state, "GET",
|
|
143
|
-
|
|
158
|
+
var _a;
|
|
159
|
+
const response = await request(state, "GET", "assets", {
|
|
160
|
+
query: { asset_type: "survey" }
|
|
161
|
+
});
|
|
162
|
+
console.log("\u2713", (_a = response.body.results) == null ? void 0 : _a.length, "forms fetched.");
|
|
144
163
|
return prepareNextState(state, response);
|
|
145
164
|
};
|
|
146
165
|
}
|
|
147
|
-
function getSubmissions(formId, options
|
|
166
|
+
function getSubmissions(formId, options) {
|
|
148
167
|
return async (state) => {
|
|
149
|
-
const [resolvedFormId, resolvedOptions] = expandReferences(
|
|
168
|
+
const [resolvedFormId, resolvedOptions = {}] = expandReferences(
|
|
150
169
|
state,
|
|
151
170
|
formId,
|
|
152
171
|
options
|
|
153
172
|
);
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
} else {
|
|
160
|
-
query.query = JSON.stringify(resolvedOptions.query);
|
|
161
|
-
}
|
|
173
|
+
const { query, limit, pageSize, sort, start } = resolvedOptions;
|
|
174
|
+
const path = `/assets/${resolvedFormId}/data/`;
|
|
175
|
+
const qs = {};
|
|
176
|
+
if (query) {
|
|
177
|
+
qs.query = maybeStringify(query);
|
|
162
178
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
179
|
+
if (sort) {
|
|
180
|
+
qs.sort = maybeStringify(sort);
|
|
181
|
+
}
|
|
182
|
+
const requestOptions = {
|
|
183
|
+
query: { ...qs },
|
|
184
|
+
start,
|
|
185
|
+
limit,
|
|
186
|
+
pageSize
|
|
187
|
+
};
|
|
188
|
+
const result = await requestWithPagination(
|
|
189
|
+
state,
|
|
190
|
+
path,
|
|
191
|
+
requestOptions
|
|
192
|
+
);
|
|
193
|
+
console.log("\u2713", result == null ? void 0 : result.length, "submissions fetched.");
|
|
194
|
+
return composeNextState2(state, result);
|
|
169
195
|
};
|
|
170
196
|
}
|
|
171
197
|
function getDeploymentInfo(formId) {
|
|
@@ -183,12 +209,25 @@ var http_exports = {};
|
|
|
183
209
|
__export(http_exports, {
|
|
184
210
|
get: () => get,
|
|
185
211
|
post: () => post,
|
|
186
|
-
put: () => put
|
|
212
|
+
put: () => put,
|
|
213
|
+
request: () => request2
|
|
187
214
|
});
|
|
188
215
|
import { expandReferences as expandReferences2 } from "@openfn/language-common/util";
|
|
189
|
-
function
|
|
216
|
+
function request2(method, path, options = {}) {
|
|
217
|
+
return async (state) => {
|
|
218
|
+
const [resolvedMethod, resolvedPath, resolvedOptions = {}] = expandReferences2(state, method, path, options);
|
|
219
|
+
const response = await request(
|
|
220
|
+
state,
|
|
221
|
+
resolvedMethod,
|
|
222
|
+
resolvedPath,
|
|
223
|
+
resolvedOptions
|
|
224
|
+
);
|
|
225
|
+
return prepareNextState(state, response);
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function get(path, options) {
|
|
190
229
|
return async (state) => {
|
|
191
|
-
const [resolvedPath, resolvedOptions] = expandReferences2(
|
|
230
|
+
const [resolvedPath, resolvedOptions = {}] = expandReferences2(
|
|
192
231
|
state,
|
|
193
232
|
path,
|
|
194
233
|
options
|
|
@@ -202,9 +241,9 @@ function get(path, options = {}) {
|
|
|
202
241
|
return prepareNextState(state, response);
|
|
203
242
|
};
|
|
204
243
|
}
|
|
205
|
-
function post(path, data, options
|
|
244
|
+
function post(path, data, options) {
|
|
206
245
|
return async (state) => {
|
|
207
|
-
const [resolvedPath, resolvedData, resolvedOptions] = expandReferences2(
|
|
246
|
+
const [resolvedPath, resolvedData, resolvedOptions = {}] = expandReferences2(
|
|
208
247
|
state,
|
|
209
248
|
path,
|
|
210
249
|
data,
|
|
@@ -223,9 +262,9 @@ function post(path, data, options = {}) {
|
|
|
223
262
|
return prepareNextState(state, response);
|
|
224
263
|
};
|
|
225
264
|
}
|
|
226
|
-
function put(path, data, options
|
|
265
|
+
function put(path, data, options) {
|
|
227
266
|
return async (state) => {
|
|
228
|
-
const [resolvedPath, resolvedData, resolvedOptions] = expandReferences2(
|
|
267
|
+
const [resolvedPath, resolvedData, resolvedOptions = {}] = expandReferences2(
|
|
229
268
|
state,
|
|
230
269
|
path,
|
|
231
270
|
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.1.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,36 @@ 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" } } });
|
|
32
|
+
* @example <caption>Get form submissions with sorting</caption>
|
|
33
|
+
* getSubmissions('aXecHjmbATuF6iGFmvBLBX', { sort: { _submission_time: -1 } });
|
|
34
|
+
* @example <caption>Get form submissions with specific start index</caption>
|
|
35
|
+
* getSubmissions('aXecHjmbATuF6iGFmvBLBX', { start: 10 });
|
|
37
36
|
* @function
|
|
38
37
|
* @public
|
|
39
38
|
* @param {string} formId - Form Id to get the specific submissions
|
|
40
|
-
* @param {object} [options={}] -
|
|
39
|
+
* @param {object} [options={}] - Options to control the request
|
|
40
|
+
* @param {object} [options.sort] - Field and direction to sort submissions by.
|
|
41
|
+
* @param {object} [options.query] - Query options to filter the submissions. See query operators {@link http://docs.mongodb.org/manual/reference/operator/query/.}
|
|
42
|
+
* @param {number} [options.start=0] - The index of the first submission to return.
|
|
43
|
+
* @param {number} [options.limit=30000] - Maximum number of submissions to fetch. Pass Infinity to disable the limit and download all submissions
|
|
44
|
+
* @param {number} [options.pageSize=10000] - Limits the size of each page of submissions. Maximum value is 30000.
|
|
41
45
|
* @state data - an array of submission objects
|
|
42
46
|
* @returns {Operation}
|
|
43
47
|
*/
|
|
44
|
-
export function getSubmissions(formId: string, options?:
|
|
48
|
+
export function getSubmissions(formId: string, options?: {
|
|
49
|
+
sort?: object;
|
|
50
|
+
query?: object;
|
|
51
|
+
start?: number;
|
|
52
|
+
limit?: number;
|
|
53
|
+
pageSize?: number;
|
|
54
|
+
}): Operation;
|
|
45
55
|
/**
|
|
46
56
|
* Get deployment information for a specific form. Calls `/api/v2/assets/<id>/deployment/`.
|
|
47
57
|
* @example
|
|
@@ -53,21 +63,4 @@ export function getSubmissions(formId: string, options?: object): Operation;
|
|
|
53
63
|
* @returns {Operation}
|
|
54
64
|
*/
|
|
55
65
|
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";
|
|
66
|
+
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,6 @@
|
|
|
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 function maybeStringify(query: any): string;
|
|
5
|
+
export const DEFAULT_LIMIT: 30000;
|
|
6
|
+
export const DEFAULT_PAGE_SIZE: 10000;
|
package/types/Utils.d.ts
DELETED