Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .eslintrc.js

This file was deleted.

10 changes: 5 additions & 5 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ _text_

### Checklist

- [ ] Have written Documentation
- _If not needed, explain why, otherwise remove this bullet point_
- [ ] Has tests coverage
- _If not needed, explain why, otherwise remove this bullet point_
- [ ] Have written Documentation
- _If not needed, explain why, otherwise remove this bullet point_
- [ ] Has tests coverage
- _If not needed, explain why, otherwise remove this bullet point_

---

### Known issues

<!-- Remove if not applicable -->

- _issue_
- _issue_

---

Expand Down
17 changes: 10 additions & 7 deletions .github/workflows/dhis2-verify-commits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ jobs:
with:
node-version: 20.x
cache: 'yarn'
- run: yarn install --frozen-lockfile
- run: yarn add -W @commitlint/config-conventional
- id: commitlint
run: echo ::set-output name=config_path::$(node -e "process.stdout.write(require('@dhis2/cli-style').config.commitlint)")
- uses: JulienKode/pull-request-name-linter-action@v0.5.0
with:
configuration-path: ${{ steps.commitlint.outputs.config_path }}
run: |
curl https://raw.githubusercontent.com/dhis2/cli-style/refs/heads/master/config/commitlint.config.js -o commitlint.config.js
- uses: JulienKode/pull-request-name-linter-action@v20.1.0 # uses config from commitlint.config.js by default

lint-commits:
runs-on: ubuntu-latest
Expand All @@ -31,9 +30,13 @@ jobs:
with:
node-version: 20.x
cache: 'yarn'
- run: yarn install --frozen-lockfile
- run: yarn add -W @commitlint/config-conventional@13
- id: commitlint
run: echo ::set-output name=config_path::$(node -e "process.stdout.write(require('@dhis2/cli-style').config.commitlint)")
run: |
curl https://raw.githubusercontent.com/dhis2/cli-style/refs/heads/master/config/commitlint.config.js -o commitlint.config.js
echo ::set-output name=config_path::commitlint.config.js
- run: echo ${{ steps.commitlint.outputs.config_path }}
- run: cat ${{ steps.commitlint.outputs.config_path }}
- uses: wagoid/commitlint-github-action@v4
with:
configFile: ${{ steps.commitlint.outputs.config_path }}
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
yarn lint-staged
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/
5 changes: 0 additions & 5 deletions .prettierrc.js

This file was deleted.

10 changes: 10 additions & 0 deletions .prettierrc.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import prettierConfig from '@dhis2/config-prettier'

/**
* @type {import("prettier").Config}
*/
const config = {
...prettierConfig,
}

export default config
549 changes: 227 additions & 322 deletions CHANGELOG.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ under the [LIBS](https://jira.dhis2.org/projects/LIBS) project.

Deep links:

- [Bug](https://jira.dhis2.org/secure/CreateIssueDetails!init.jspa?pid=10700&issuetype=10006&components=11024)
- [Feature](https://jira.dhis2.org/secure/CreateIssueDetails!init.jspa?pid=10700&issuetype=10300&components=11024)
- [Task](https://jira.dhis2.org/secure/CreateIssueDetails!init.jspa?pid=10700&issuetype=10003&components=11024)
- [Bug](https://jira.dhis2.org/secure/CreateIssueDetails!init.jspa?pid=10700&issuetype=10006&components=11024)
- [Feature](https://jira.dhis2.org/secure/CreateIssueDetails!init.jspa?pid=10700&issuetype=10300&components=11024)
- [Task](https://jira.dhis2.org/secure/CreateIssueDetails!init.jspa?pid=10700&issuetype=10003&components=11024)
14 changes: 7 additions & 7 deletions docs/advanced/CustomDataProvider.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ The [CustomDataProvider](https://github.com/dhis2/app-runtime/blob/master/servic

The CustomDataProvider accepts four props:

- children: a valid React node.
- [data](https://github.com/dhis2/app-runtime/blob/master/services/data/src/links/CustomDataLink.ts#L15): an object that defines the replies for certain resources. See below for examples.
- [options](https://github.com/dhis2/app-runtime/blob/master/services/data/src/links/CustomDataLink.ts#L18): an object with the keys `loadForever` and `failOnMiss`. Set `loadForever` to `true` to force queries to keep loading indefinitely. Set `failOnMiss` to `true` to throw an error for any requests that have no matching reply defined in `data`
- queryClientOptions: allows you to override the default queryClientOptions, see the [react-query docs](https://react-query.tanstack.com/reference/QueryClient) for the format and available options.
- children: a valid React node.
- [data](https://github.com/dhis2/app-runtime/blob/master/services/data/src/links/CustomDataLink.ts#L15): an object that defines the replies for certain resources. See below for examples.
- [options](https://github.com/dhis2/app-runtime/blob/master/services/data/src/links/CustomDataLink.ts#L18): an object with the keys `loadForever` and `failOnMiss`. Set `loadForever` to `true` to force queries to keep loading indefinitely. Set `failOnMiss` to `true` to throw an error for any requests that have no matching reply defined in `data`
- queryClientOptions: allows you to override the default queryClientOptions, see the [react-query docs](https://react-query.tanstack.com/reference/QueryClient) for the format and available options.

## Static replies

Expand All @@ -35,6 +35,6 @@ Instead of defining a static response it is also possible to supply a function.

The supplied callback will be called with three arguments, namely:

- [type](https://github.com/dhis2/app-runtime/blob/master/services/data/src/engine/types/ExecuteOptions.ts#L4)
- [query](https://github.com/dhis2/app-runtime/blob/master/services/data/src/engine/types/Query.ts#L15)
- [options](https://github.com/dhis2/app-runtime/blob/master/services/data/src/engine/types/DataEngineLink.ts#L5)
- [type](https://github.com/dhis2/app-runtime/blob/master/services/data/src/engine/types/ExecuteOptions.ts#L4)
- [query](https://github.com/dhis2/app-runtime/blob/master/services/data/src/engine/types/Query.ts#L15)
- [options](https://github.com/dhis2/app-runtime/blob/master/services/data/src/engine/types/DataEngineLink.ts#L5)
151 changes: 150 additions & 1 deletion docs/advanced/DataEngine.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,152 @@
# Data Engine

> **TODO** Add documentation
This document describes the `@dhis2/data-engine` package — the standalone data engine used by the runtime. The engine provides a small, framework-agnostic interface for executing queries and mutations through a pluggable transport link.

Manually constructing a Data Engine is most useful when running server-side javascript, for example in Node or in server-side Next.js code.

## Overview

The `DataEngine` class is a thin wrapper around transport "links". Links are responsible for translating resource queries into network requests. The most common link to use is `RestAPILink` which handles communication with the DHIS2 Rest API

The DataEngine can be used to imperatively execute [Query](../types/Query.md) and [Mutation](../types/Mutation.md) objects. The engine is used internally by the App Runtime, which should be preferred when in a React context.

## Installation

Install the package from npm:

```bash
npm install @dhis2/data-engine
# or
yarn add @dhis2/data-engine
```

## Quick start

```typescript
import {
DataEngine,
RestAPILink,
type DataEngineConfig,
} from '@dhis2/data-engine'

// Specify the configuration object and construct a RestAPILink
const config: DataEngineConfig = {
baseUrl: 'https://my-dhis2-server.com',
apiVersion: 42,
apiToken: 'MY_PERSONAL_ACCESS_TOKEN',
}
const link = new RestAPILink(config)

// Construct the DataEngine using the RestAPILink
const engine = new DataEngine(link)

// Simple query
const data = await engine.query({
me: { resource: 'me' },
})

// Simple mutation
const result = await engine.mutate({
type: 'create',
resource: 'dataValues',
data: {
/* ... */
},
})

// Convenience fetch helpers
await engine.get('/api/organisationUnits')
await engine.post('/api/resource', { some: 'body' })
```

## Configuration

The following configuration properties are supported:

| property | required | description |
| --------------- | -------: | ------------------------------------------------------------------------------------------------------------------ |
| `baseUrl` | yes | Base URL of the DHIS2 server (for example: https://play.dhis2.org) |
| `apiVersion` | yes | Numeric API version to target (for example: `38`) |
| `serverVersion` | no | Optional server version object with fields `major`, `minor`, optional `patch`, and `full` string (see table below) |
| `apiToken` | no | Optional API token (personal access token) used for authentication when using token-based auth |

Server version fields:

| property | required | description |
| -------- | -------: | ----------------------------------------- |
| `major` | yes | Major server version number |
| `minor` | yes | Minor server version number |
| `patch` | no | Patch version number (optional) |
| `full` | yes | Full server version string, e.g. `2.38.0` |

The server version is used to toggle different features which are only supported with certain DHIS2 server versions. If it is not supplied, the DataEngine will operate assuming an older version of DHIS2.

In the future, the DataEngine may optionally fetch this automatically from the server's `/system/info` endpoint

## Authentication

Unlike in browser contexts, where authentication is transmitted via HTTP-only session cookies, server environments need another way to authenticate API requests. To accomplish this, the config object supports an `apiToken` property which should be a Personal Access Token granting access to the target DHIS2 instance.

**SECURITY NOTE** Do NOT use personal access tokens in browser environments. PATs should always be stored securely and passed to server scripts in secure ways, for example using environment variables or secret vaults instead of command-line arguments.

## API

The primary exported class is `DataEngine` which can be constructed as follows:

`new DataEngine(link: DataEngineLink)` — Construct a new engine using the given link.

To construct a RestAPI link, pass a configuration object to the following constructor:

`new RestAPILink(config: DataEngineConfig)` - Construct a new RestAPILink for communication with a specific DHIS2 server

The Engine returned has the following methods:

- `query(query: Query, options?: QueryExecuteOptions): Promise<T>` — Execute one or more resource queries. The `query` parameter is an object mapping keys to `ResourceQuery` objects; the returned value is an object mapping the same keys to their results.
- `mutate(mutation: Mutation, options?: QueryExecuteOptions): Promise<unknown>` — Execute a mutation. Supported mutation types include `create`, `update`, `delete`, etc., depending on the link implementation.
- `fetch(path: string, init?: RequestInit, executeOptions?: QueryExecuteOptions)` — Run a fetch-style request through the engine. Absolute URLs are not supported; paths are resolved against the server base.
- Convenience methods: `get`, `post`, `put`, `patch`, `jsonPatch`, `delete` — thin wrappers around `fetch` with the appropriate HTTP method.

### `QueryExecuteOptions`

Common options supported by `query` and `mutate`:

- `variables` — Object with variables to resolve dynamic queries.
- `signal` — `AbortSignal` to cancel the request.
- `onComplete` — Callback invoked with the result when the request completes.
- `onError` — Callback invoked when the request errors.

### Notes on `fetch`

- `fetch` will map the provided `init` (method, body, headers) to either a `query` or `mutation` call internally. If the computed type is `read` it will call `query` and return the `result` property; otherwise it will call `mutate`.
- `jsonPatch(path, patches, options)` sends a PATCH request with `Content-Type: application/json-patch+json`.
- Absolute URLs (paths containing `://`) are rejected to avoid cross-origin usage through the engine.

## Examples

Query multiple resources in a single call:

```javascript
const data = await engine.query({
users: { resource: 'users', params: { paging: false } },
me: { resource: 'me' },
})

console.log(data.users) // list of users
console.log(data.me) // current user
```

Using `fetch` convenience methods:

```javascript
const unit = await engine.get('/api/organisationUnits/abc')

await engine.jsonPatch('/api/resource/123', [
{ op: 'replace', path: '/name', value: 'New name' },
])
```

## Using in React

The DataEngine has been moved to a standalone package without a React dependency. This makes it easier to reuse in non-React contexts.

In React contexts using the DHIS2 App Platform, the engine is created automatically and can be accessed using the [useDataEngine hook](../hooks/useDataEngine.md).
6 changes: 3 additions & 3 deletions docs/advanced/offline/useDhis2ConnectionStatus.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ The `isConnected` value is primarily detected by the service worker, which liste

During periods when there’s no network traffic from the app, “pings” will be used **conservatively** to see if the server is reachable. There are several measures taken to limit the usage of these pings:

- While the connection status is stable, the intervals between pings will increase exponentially up to a long interval.
- Any new network traffic from the app will postpone future pings.
- If the app is not focused, no pings will be sent.
- While the connection status is stable, the intervals between pings will increase exponentially up to a long interval.
- Any new network traffic from the app will postpone future pings.
- If the app is not focused, no pings will be sent.

### Supported versions

Expand Down
6 changes: 3 additions & 3 deletions docs/advanced/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ Services are purely a concept **internal** to `@dhis2/app-runtime` - you don't n

Internally, `@dhis2/app-runtime` is composed of several functionally-isolated **services**. Each of these services is a private (unpublished) npm package which is bundled into the final `@dhis2/app-runtime` library at build-time. Each services exposes a relevant Context provider as well as various Hook and Component interfaces which are re-exported by the `@dhis2/app-runtime` public package. There are currently only 2 services, but more will be added soon.

- [**Config**](https://github.com/dhis2/app-runtime/blob/master/services/config) - Provide access to basic application configuration values from anywhere within an application. _NOTE: this services is special because it can be a dependency of other services!_
- [**Data**](https://github.com/dhis2/app-runtime/blob/master/services/data) - Provides a declarative data access interface and data engine which will eventually handle caching, deduplication, and fetching of DHIS2 data (anything accessible through the DHIS2 Core API)
- [**Alerts**](https://github.com/dhis2/app-runtime/blob/master/services/alerts) - Provides a `useAlerts` hook to read a central alerts-context and a `useAlert` hook to add alerts to the alerts-context.
- [**Config**](https://github.com/dhis2/app-runtime/blob/master/services/config) - Provide access to basic application configuration values from anywhere within an application. _NOTE: this services is special because it can be a dependency of other services!_
- [**Data**](https://github.com/dhis2/app-runtime/blob/master/services/data) - Provides a declarative data access interface and data engine which will eventually handle caching, deduplication, and fetching of DHIS2 data (anything accessible through the DHIS2 Core API)
- [**Alerts**](https://github.com/dhis2/app-runtime/blob/master/services/alerts) - Provides a `useAlerts` hook to read a central alerts-context and a `useAlert` hook to add alerts to the alerts-context.
4 changes: 2 additions & 2 deletions docs/components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

As an alternative to [React Hooks](../hooks/README.md), the DHIS2 Application Runtime support render-prop React components. The following Components are supported:

- [**DataQuery**](DataQuery) - a data fetching Component wrapper around the [useDataQuery](../hooks/useDataQuery) hook.
- [**DataMutation**](DataMutation) - a Component wrapper around the [useDataMutation](../hooks/useDataMutation) hook.
- [**DataQuery**](DataQuery) - a data fetching Component wrapper around the [useDataQuery](../hooks/useDataQuery) hook.
- [**DataMutation**](DataMutation) - a Component wrapper around the [useDataMutation](../hooks/useDataMutation) hook.
6 changes: 3 additions & 3 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ This library uses the official React Context API (introduced in 16.3) and React

The following must be polyfilled to support older and non-compliant browsers (i.e. IE11):

- es6.promise (i.e. [core-js/features/promise](https://github.com/zloirock/core-js))
- window.fetch (i.e. [whatwg-fetch](https://github.com/github/fetch))
- AbortController / AbortSignal (i.e. [abortcontroller-polyfill](https://www.npmjs.com/package/abortcontroller-polyfill))
- es6.promise (i.e. [core-js/features/promise](https://github.com/zloirock/core-js))
- window.fetch (i.e. [whatwg-fetch](https://github.com/github/fetch))
- AbortController / AbortSignal (i.e. [abortcontroller-polyfill](https://www.npmjs.com/package/abortcontroller-polyfill))
8 changes: 4 additions & 4 deletions docs/guides/display-alerts.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ In this guide we'll walk you through, step by step, how to display an alert in y

To be able to implement the alert in your application, you need to have the following:

- A web application built using the DHIS2 Application Platform
- A basic understanding of React hooks
- A web application built using the DHIS2 Application Platform
- A basic understanding of React hooks

If you do not yet have a web application built using the DHIS2 Application Platform, you can follow the [Getting Started](/docs/quickstart/quickstart-web) guide to create a new application.

Expand Down Expand Up @@ -114,5 +114,5 @@ You can read more about the `useAlert` hook in the [official documentation](/doc

## Additional Resources

- [AlertBar Component Documentation](/docs/ui/components/alertbar)
- [AlertBar Demo](pathname:///demo/?path=/story/alert-bar--states)
- [AlertBar Component Documentation](/docs/ui/components/alertbar)
- [AlertBar Demo](pathname:///demo/?path=/story/alert-bar--states)
4 changes: 2 additions & 2 deletions docs/guides/using-dataquery.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ In this guide, we'll walk you through, step by step, how to use the `useDataQuer

To be able to use the `useDataQuery` hook in your application, you need to have the following:

- A web application built using the DHIS2 Application Platform
- A basic understanding of React hooks
- A web application built using the DHIS2 Application Platform
- A basic understanding of React hooks

If you do not yet have a web application built using the DHIS2 Application Platform, you can follow the [Getting Started](/docs/quickstart/quickstart-web) guide to create a new application.

Expand Down
Loading
Loading