Skip to content

Commit

Permalink
Merge pull request #961 from OpenFn/feature/695-kobotoolbox-common
Browse files Browse the repository at this point in the history
kobotoolbox: migrate to common2.0
  • Loading branch information
josephjclark authored Feb 3, 2025
2 parents f033e36 + c0fe0cf commit 28fa68f
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 81 deletions.
29 changes: 22 additions & 7 deletions packages/kobotoolbox/ast.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"name": "getForms",
"params": [
"params",
"options",
"callback"
],
"docs": {
Expand All @@ -25,12 +25,16 @@
},
{
"title": "param",
"description": "Query, Headers and Authentication parameters",
"description": "Optional headers and query for the request",
"type": {
"type": "NameExpression",
"name": "object"
"type": "OptionalType",
"expression": {
"type": "NameExpression",
"name": "RequestOptions"
}
},
"name": "params"
"name": "options",
"default": "{}"
},
{
"title": "param",
Expand Down Expand Up @@ -437,7 +441,7 @@
"operation"
],
"docs": {
"description": "Scopes an array of data based on a JSONPath.\nUseful when the source data has `n` items you would like to map to\nan operation.\nThe operation will receive a slice of the data based of each item\nof the JSONPath provided.\n\nIt also ensures the results of an operation make their way back into\nthe state's references.",
"description": "Iterates over an array of items and invokes an operation upon each one, where the state\nobject is _scoped_ so that state.data is the item under iteration.\nThe rest of the state object is untouched and can be referenced as usual.\nYou can pass an array directly, or use lazy state or a JSONPath string to\nreference a slice of state.",
"tags": [
{
"title": "public",
Expand All @@ -451,7 +455,18 @@
},
{
"title": "example",
"description": "each(\"$.[*]\",\n create(\"SObject\",\n field(\"FirstName\", sourceValue(\"$.firstName\"))\n )\n)"
"description": "each(\n $.data,\n // Inside the callback operation, `$.data` is scoped to the item under iteration\n insert(\"patient\", {\n patient_name: $.data.properties.case_name,\n patient_id: $.data.case_id,\n })\n);",
"caption": "Using lazy state ($) to iterate over items in state.data and pass each into an \"insert\" operation"
},
{
"title": "example",
"description": "each(\n $.data,\n insert(\"patient\", (state) => ({\n patient_id: state.data.case_id,\n ...state.data\n }))\n);",
"caption": "Iterate over items in state.data and pass each one into an \"insert\" operation"
},
{
"title": "example",
"description": "each(\n \"$.data[*]\",\n insert(\"patient\", (state) => ({\n patient_name: state.data.properties.case_name,\n patient_id: state.data.case_id,\n }))\n);",
"caption": "Using JSON path to iterate over items in state.data and pass each one into an \"insert\" operation"
},
{
"title": "param",
Expand Down
2 changes: 1 addition & 1 deletion packages/kobotoolbox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"configuration-schema.json"
],
"dependencies": {
"@openfn/language-common": "1.15.2"
"@openfn/language-common": "workspace:*"
},
"devDependencies": {
"@openfn/simple-ast": "0.4.1",
Expand Down
106 changes: 35 additions & 71 deletions packages/kobotoolbox/src/Adaptor.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import {
execute as commonExecute,
composeNextState,
expandReferences,
http, // IMPORTANT: this is the OLD axios-based HTTP
} from '@openfn/language-common';
import { execute as commonExecute } from '@openfn/language-common';
import { expandReferences } from '@openfn/language-common/util';

import * as util from './Utils';

/**
* Options object
* @typedef {Object} RequestOptions
* @property {object} query - An object of query parameters to be encoded into the URL
* @property {object} headers - An object of all request headers
* @property {string} [parseAs='json'] - The response format to parse (e.g., 'json', 'text', or 'stream')
*/

/**
* Execute a sequence of operations.
Expand Down Expand Up @@ -40,33 +46,19 @@ export function execute(...operations) {
* return state;
* });
* @function
* @param {object} params - Query, Headers and Authentication parameters
* @param {RequestOptions} [options={}] - Optional headers and query for the request
* @param {function} callback - (Optional) Callback function to execute after fetching form list
* @returns {Operation}
*/
export function getForms(params, callback) {
return state => {
const resolvedParams = expandReferences(params)(state);

const { baseURL, apiVersion, username, password } = state.configuration;

const url = `${baseURL}/api/${apiVersion}/assets/?format=json`;
const auth = { username, password };

const config = {
url,
params: resolvedParams,
auth,
};

return http
.get(config)(state)
.then(response => {
console.log('✓', response.data.count, 'forms fetched.');
const nextState = composeNextState(state, response.data);
if (callback) return callback(nextState);
return nextState;
});
export function getForms(options = {}, callback) {
return async state => {
const [resolvedOptions] = expandReferences(state, options);

const url = `/assets/?format=json`;

const response = await util.request(state, 'GET', url, resolvedOptions);
console.log('✓', response.body.count, 'forms fetched.');
return util.prepareNextState(state, response, callback);
};
}

Expand All @@ -84,30 +76,16 @@ export function getForms(params, callback) {
* @returns {Operation}
*/
export function getSubmissions(params, callback) {
return state => {
const resolvedParams = expandReferences(params)(state);
return async state => {
const [resolvedParams] = expandReferences(state, params);

const { baseURL, apiVersion, username, password } = state.configuration;
const { formId } = resolvedParams;

const url = `${baseURL}/api/${apiVersion}/assets/${formId}/data/?format=json`;
const auth = { username, password };

const config = {
url,
params: resolvedParams.query,
auth,
};

return http
.get(config)(state)
.then(response => {
console.log('✓', response.data.count, 'submissions fetched.');
const url = `/assets/${formId}/data/?format=json`;

const nextState = composeNextState(state, response.data);
if (callback) return callback(nextState);
return nextState;
});
const response = await util.request(state, 'GET', url, resolvedParams);
console.log('✓', response.body.count, 'forms fetched.');
return util.prepareNextState(state, response, callback);
};
}

Expand All @@ -125,30 +103,16 @@ export function getSubmissions(params, callback) {
* @returns {Operation}
*/
export function getDeploymentInfo(params, callback) {
return state => {
const resolvedParams = expandReferences(params)(state);
return async state => {
const [resolvedParams] = expandReferences(state, params);

const { baseURL, apiVersion, username, password } = state.configuration;
const { formId } = resolvedParams;

const url = `${baseURL}/api/${apiVersion}/assets/${formId}/deployment/?format=json`;
const auth = { username, password };

const config = {
url,
params: resolvedParams.query,
auth,
};

return http
.get(config)(state)
.then(response => {
console.log('✓', 'deployment information fetched.');
const url = `/assets/${formId}/deployment/?format=json`;

const nextState = composeNextState(state, response.data);
if (callback) return callback(nextState);
return nextState;
});
const response = await util.request(state, 'GET', url, resolvedParams);
console.log('✓', 'deployment information fetched.');
return util.prepareNextState(state, response, callback);
};
}

Expand All @@ -162,7 +126,7 @@ export {
fields,
fn,
fnIf,
http, // IMPORTANT: this is the OLD axios-based HTTP. The public documentation for this will be wrong.
http,
group,
lastReferenceValue,
merge,
Expand Down
37 changes: 37 additions & 0 deletions packages/kobotoolbox/src/Utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { composeNextState } from '@openfn/language-common';
import {
request as commonRequest,
makeBasicAuthHeader,
logResponse,
} from '@openfn/language-common/util';

export const prepareNextState = (state, response, callback = s => s) => {
const { body, ...responseWithoutBody } = response;
const nextState = {
...composeNextState(state, response.body),
response: responseWithoutBody,
};
return callback(nextState);
};

export async function request(state, method, path, opts) {
const { baseURL, apiVersion, username, password } = state.configuration;

const { data = {}, query = {}, headers = {}, parseAs = 'json' } = opts;

const authHeaders = makeBasicAuthHeader(username, password);

const options = {
body: data,
headers: {
'Content-Type': 'application/json',
...authHeaders,
...headers,
},
query,
parseAs,
baseUrl: `${baseURL}/api/${apiVersion}`,
};

return commonRequest(method, path, options).then(logResponse);
}
4 changes: 2 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 28fa68f

Please sign in to comment.