Skip to content
7 changes: 7 additions & 0 deletions .changeset/hip-planes-see.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@openfn/language-dhis2': minor
---

Add async option to `tracker.import` and
Add paging examples to
`tracker.export()`
19 changes: 10 additions & 9 deletions packages/dhis2/src/tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import * as util from './util.js';
* @param {string} strategy - The effect the import should have. Can either be CREATE, UPDATE, CREATE_AND_UPDATE and DELETE.
* @param {object} payload - The data to be imported.
* @param {TrackerOptions} [options] - An optional object containing parseAs, and apiVersion, and queries for the request
* @param {boolean} [options.async=false] - Whether to perform the import asynchronously. Defaults to false. See [Sync and async imports](https://docs.dhis2.org/en/develop/using-the-api/dhis-core-version-master/tracker.html#sync-and-async)
* @state {DHIS2State}
* @returns {Operation}
*/
Expand All @@ -57,7 +58,7 @@ function _import(strategy, payload, options = {}) {
const [resolvedStrategy, resolvedPayload, resolvedOptions] =
expandReferences(state, strategy, payload, options);

const { apiVersion, parseAs, ...query } = resolvedOptions;
const { apiVersion, parseAs, async = false, ...query } = resolvedOptions;

const response = await util.request(state.configuration, {
method: 'POST',
Expand All @@ -67,14 +68,14 @@ function _import(strategy, payload, options = {}) {
...resolvedOptions,
resolvedStrategy,
},
'tracker'
'tracker',
),
options: {
apiVersion,
parseAs,
query: {
...query,
async: false,
async,
},
},
data: resolvedPayload,
Expand All @@ -95,37 +96,37 @@ export { _import as import };
* @example <caption>Export all enrollment resources</caption>
* tracker.export('enrollments', {orgUnit: 'TSyzvBiovKh'});
* @example <caption>Export all events</caption>
* tracker.export('events')
* tracker.export('events', { paging: false})
* @function
* @param {string} path - Path to the resource, relative to the /tracker endpoint
* @param {object} query - An object of query parameters to be encoded into the URL
* @param {object} query - An object of query parameters to be encoded into the URL. Can include [pagination parameters](https://docs.dhis2.org/en/develop/using-the-api/dhis-core-version-master/tracker.html#request-parameters-for-pagination), filters, etc.
* @param {string} [query.order] - Comma-separated field:sortDirection pairs, e.g. `createdAt:desc`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only query property we're actually documenting now. Why is this one so important?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I must have missed it. i removed it

* @param {TrackerOptions} [options] - An optional object containing parseAs, and apiVersion for the request
* @state {DHIS2State}
* @returns {Operation}
*/
function _export(path, query, options = {}) {
function _export(path, query = {}, options = {}) {
return async state => {
console.log('Preparing tracker export operation...');

const [resolvedPath, resolvedQuery, resolvedOptions] = expandReferences(
state,
path,
query,
options
options,
);

const response = await util.request(state.configuration, {
method: 'GET',
path: util.prefixVersionToPath(
state.configuration,
resolvedOptions,
`tracker/${resolvedPath}`
`tracker/${resolvedPath}`,
),
options: {
...resolvedOptions,
query: {
...resolvedQuery,
async: false,
},
},
});
Expand Down
79 changes: 78 additions & 1 deletion packages/dhis2/test/integration.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { expect } from 'chai';
import crypto from 'node:crypto';
import { execute, create, update, upsert, get } from '../dist/index.js';
import {
execute,
tracker,
combine,
create,
update,
upsert,
each,
get,
fn,
} from '../src/index.js';

const getRandomProgramPayload = () => {
const name = crypto.randomBytes(16).toString('hex');
Expand Down Expand Up @@ -471,4 +481,71 @@ describe('Integration tests', () => {
);
});
});
describe('tracker', () => {
it('should export 50 events by default', async () => {
// v2.41+ for older version `skipPaging: true`
const state = {
configuration,
};
const finalState = await execute(tracker.export('events'))(state);

expect(finalState.data.instances.length).to.eql(50);
}).timeout(2e4);

it('should export 1000 events with pageSize 1000', async () => {
const state = {
configuration,
};
const { data } = await execute(
tracker.export('events', { totalPages: true, pageSize: 1e3 }),
)(state);

expect(Object.keys(data).sort()).to.eql([
'instances',
'page',
'pageCount',
'pageSize',
'total',
]);
expect(data.instances.length).to.eql(1000);
}).timeout(2e4);

it('should export all events with pagination', async () => {
const state = {
configuration,
};
const { data, results } = await execute(
tracker.export('events', { totalPages: true, pageSize: 1e4 }),
fn(state => {
console.log(Object.keys(state.data));
state.results = state.data.instances;
const { page, pageSize, pageCount, total } = state.data;
const remainingPages = pageCount - page;

state.pages = Array.from(
{ length: remainingPages },
(_, i) => page + i + 1,
);
state.pageSize = pageSize;
return state;
}),

each(
state => state.pages,
combine(
tracker.export('events', state => ({
pageSize: state.pageSize,
page: state.data,
})),
fn(state => {
state.results = state.results.concat(state.data.instances);
return state;
}),
),
),
)(state);

expect(results).to.be.greaterThan(3e4);
}).timeout(5e4);
});
});
Loading
Loading