@dhis2/app-service-data 3.17.1 → 3.18.0-beta.2
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/README.md +104 -0
- package/build/cjs/react/hooks/useDataQuery.js +4 -0
- package/build/es/react/hooks/useDataQuery.js +5 -0
- package/build/types/react/hooks/useDataQuery.d.ts +6 -2
- package/package.json +5 -4
- package/.gitignore +0 -5
- package/d2.config.js +0 -9
- package/jest.config.js +0 -14
- package/src/__tests__/integration.test.tsx +0 -80
- package/src/__tests__/mutations.test.tsx +0 -71
- package/src/index.ts +0 -5
- package/src/react/components/CustomDataProvider.tsx +0 -33
- package/src/react/components/DataMutation.tsx +0 -24
- package/src/react/components/DataProvider.test.tsx +0 -22
- package/src/react/components/DataProvider.tsx +0 -58
- package/src/react/components/DataQuery.tsx +0 -26
- package/src/react/components/index.ts +0 -4
- package/src/react/context/DataContext.tsx +0 -5
- package/src/react/context/defaultDataContext.test.ts +0 -46
- package/src/react/context/defaultDataContext.ts +0 -9
- package/src/react/hooks/index.ts +0 -3
- package/src/react/hooks/mergeAndCompareVariables.test.ts +0 -65
- package/src/react/hooks/mergeAndCompareVariables.ts +0 -32
- package/src/react/hooks/stableVariablesHash.test.ts +0 -53
- package/src/react/hooks/stableVariablesHash.ts +0 -56
- package/src/react/hooks/useDataEngine.ts +0 -8
- package/src/react/hooks/useDataMutation.test.tsx +0 -296
- package/src/react/hooks/useDataMutation.ts +0 -42
- package/src/react/hooks/useDataQuery.test.tsx +0 -1029
- package/src/react/hooks/useDataQuery.ts +0 -170
- package/src/react/hooks/useQueryExecutor.test.tsx +0 -195
- package/src/react/hooks/useQueryExecutor.ts +0 -99
- package/src/react/hooks/useStaticInput.test.ts +0 -101
- package/src/react/hooks/useStaticInput.ts +0 -27
- package/src/react/index.ts +0 -2
- package/src/setupRTL.ts +0 -5
- package/src/types.ts +0 -72
- package/tsconfig.json +0 -10
package/README.md
CHANGED
|
@@ -9,3 +9,107 @@ This library is intended for use with the [DHIS2 Application Platform](https://g
|
|
|
9
9
|
This package is internal to `@dhis2/app-runtime` and generally should not be installed independently.
|
|
10
10
|
|
|
11
11
|
See [the docs](https://developers.dhis2.org/docs/app-runtime/getting-started) for more.
|
|
12
|
+
|
|
13
|
+
## TypeScript — typed query responses
|
|
14
|
+
|
|
15
|
+
`useDataQuery` infers the response type automatically from the query object. The default map covers all DHIS2 v43 metadata endpoints. Three patterns are supported:
|
|
16
|
+
|
|
17
|
+
### 1. Default — v43 types, fully inferred
|
|
18
|
+
|
|
19
|
+
Pass the query `as const` so TypeScript can see the literal resource name and field list. No type annotation needed.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { useDataQuery } from '@dhis2/app-runtime'
|
|
23
|
+
|
|
24
|
+
const { data } = useDataQuery({
|
|
25
|
+
dataElements: {
|
|
26
|
+
resource: 'dataElements',
|
|
27
|
+
params: { fields: ['id', 'name', 'valueType'] as const },
|
|
28
|
+
},
|
|
29
|
+
} as const)
|
|
30
|
+
|
|
31
|
+
// data?.dataElements.pager.page → number
|
|
32
|
+
// data?.dataElements.dataElements[0].id → string | undefined
|
|
33
|
+
// data?.dataElements.dataElements[0].valueType → ValueType (enum, not just string)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`as const` on the fields array is what allows TypeScript to narrow the element type. Without it the response type is broader but still valid. Without `as const` on the outer object, resource names are not captured as literals and field narrowing is skipped entirely.
|
|
37
|
+
|
|
38
|
+
### 2. Targeting a specific DHIS2 API version
|
|
39
|
+
|
|
40
|
+
If the target instance runs an older DHIS2 version, build the result type from that version's paths. `paths` comes from the version-specific entry point; `DeriveResourceTypeMap` and `InferQueryResult` are version-agnostic and always come from `@dhis2/api-types/utils`:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { useDataQuery } from '@dhis2/app-runtime'
|
|
44
|
+
import type { paths } from '@dhis2/api-types/v42'
|
|
45
|
+
import type {
|
|
46
|
+
DeriveResourceTypeMap,
|
|
47
|
+
InferQueryResult,
|
|
48
|
+
} from '@dhis2/api-types/utils'
|
|
49
|
+
|
|
50
|
+
const query = {
|
|
51
|
+
dataElements: {
|
|
52
|
+
resource: 'dataElements',
|
|
53
|
+
params: { fields: ['id', 'name', 'valueType'] as const },
|
|
54
|
+
},
|
|
55
|
+
} as const
|
|
56
|
+
|
|
57
|
+
type Result = InferQueryResult<typeof query, DeriveResourceTypeMap<paths>>
|
|
58
|
+
|
|
59
|
+
const { data } = useDataQuery<typeof query, Result>(query)
|
|
60
|
+
// data?.dataElements.dataElements[0].valueType → v42's ValueType
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`@dhis2/api-types/v40`, `/v41`, `/v42`, and `/v43` are all available.
|
|
64
|
+
|
|
65
|
+
### 3. Overriding the inferred type
|
|
66
|
+
|
|
67
|
+
Pass an explicit result type as the second generic to opt out of inference entirely — useful for custom endpoints or when the inferred type needs to be shaped differently:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { useDataQuery } from '@dhis2/app-runtime'
|
|
71
|
+
|
|
72
|
+
type MyResult = {
|
|
73
|
+
dataElements: {
|
|
74
|
+
pager: {
|
|
75
|
+
page: number
|
|
76
|
+
pageCount: number
|
|
77
|
+
total: number
|
|
78
|
+
pageSize: number
|
|
79
|
+
}
|
|
80
|
+
dataElements: Array<{ id: string; name: string; valueType: string }>
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const { data } = useDataQuery<typeof query, MyResult>(query)
|
|
85
|
+
// data?.dataElements.dataElements[0] → { id: string; name: string; valueType: string }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Tracker resources
|
|
89
|
+
|
|
90
|
+
Tracker endpoints (`tracker/enrollments`, `tracker/trackedEntities`, etc.) resolve to `unknown` with the default map because the spec types their responses opaquely. Extend the map manually to cover them:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { useDataQuery } from '@dhis2/app-runtime'
|
|
94
|
+
import type { paths, TrackerEnrollment } from '@dhis2/api-types'
|
|
95
|
+
import type {
|
|
96
|
+
DeriveResourceTypeMap,
|
|
97
|
+
InferQueryResult,
|
|
98
|
+
} from '@dhis2/api-types/utils'
|
|
99
|
+
|
|
100
|
+
type TrackerMap = DeriveResourceTypeMap<paths> & {
|
|
101
|
+
'tracker/enrollments': TrackerEnrollment
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const query = {
|
|
105
|
+
enrollments: {
|
|
106
|
+
resource: 'tracker/enrollments',
|
|
107
|
+
params: { fields: ['enrollment', 'status'] as const },
|
|
108
|
+
},
|
|
109
|
+
} as const
|
|
110
|
+
|
|
111
|
+
type Result = InferQueryResult<typeof query, TrackerMap>
|
|
112
|
+
|
|
113
|
+
const { data } = useDataQuery<typeof query, Result>(query)
|
|
114
|
+
// data?.enrollments.enrollments[0].status → EnrollmentStatus
|
|
115
|
+
```
|
|
@@ -9,6 +9,10 @@ var _react = require("react");
|
|
|
9
9
|
var _mergeAndCompareVariables = require("./mergeAndCompareVariables");
|
|
10
10
|
var _useDataEngine = require("./useDataEngine");
|
|
11
11
|
var _useStaticInput = require("./useStaticInput");
|
|
12
|
+
// Pre-compute the default resource→item map once from the latest API version's paths type.
|
|
13
|
+
// Consumers targeting an older DHIS2 version can pass an explicit TResult built with
|
|
14
|
+
// InferQueryResult<typeof query, DeriveResourceTypeMap<v4xPaths>> as the second generic.
|
|
15
|
+
|
|
12
16
|
const useDataQuery = (query, {
|
|
13
17
|
onComplete: userOnSuccess,
|
|
14
18
|
onError: userOnError,
|
|
@@ -3,6 +3,11 @@ import { useState, useRef, useCallback, useDebugValue } from 'react';
|
|
|
3
3
|
import { mergeAndCompareVariables } from './mergeAndCompareVariables';
|
|
4
4
|
import { useDataEngine } from './useDataEngine';
|
|
5
5
|
import { useStaticInput } from './useStaticInput';
|
|
6
|
+
|
|
7
|
+
// Pre-compute the default resource→item map once from the latest API version's paths type.
|
|
8
|
+
// Consumers targeting an older DHIS2 version can pass an explicit TResult built with
|
|
9
|
+
// InferQueryResult<typeof query, DeriveResourceTypeMap<v4xPaths>> as the second generic.
|
|
10
|
+
|
|
6
11
|
export const useDataQuery = (query, {
|
|
7
12
|
onComplete: userOnSuccess,
|
|
8
13
|
onError: userOnError,
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { paths } from '@dhis2/api-types';
|
|
2
|
+
import type { DeriveResourceTypeMap, InferQueryResult } from '@dhis2/api-types/utils';
|
|
3
|
+
import type { Query, QueryOptions } from '@dhis2/data-engine';
|
|
2
4
|
import type { QueryRenderInput } from '../../types';
|
|
3
|
-
|
|
5
|
+
type DefaultMap = DeriveResourceTypeMap<paths>;
|
|
6
|
+
export declare const useDataQuery: <Q extends Query, TResult = InferQueryResult<Q, DefaultMap>>(query: Q, { onComplete: userOnSuccess, onError: userOnError, variables: initialVariables, lazy: initialLazy, }?: QueryOptions<TResult>) => QueryRenderInput<TResult>;
|
|
7
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dhis2/app-service-data",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.18.0-beta.2",
|
|
4
4
|
"main": "./build/cjs/index.js",
|
|
5
5
|
"module": "./build/es/index.js",
|
|
6
6
|
"types": "./build/types/index.d.ts",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
|
-
"
|
|
23
|
+
"build/**"
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build:types": "tsc --emitDeclarationOnly --outDir ./build/types",
|
|
@@ -33,12 +33,13 @@
|
|
|
33
33
|
"coverage": "yarn test --coverage"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
+
"@dhis2/api-types": "43.0.0-beta.a48b417",
|
|
36
37
|
"@tanstack/react-query": "^4.36.1",
|
|
37
38
|
"prop-types": "^15.7.2"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
|
-
"@dhis2/app-service-config": "3.
|
|
41
|
-
"@dhis2/data-engine": "3.
|
|
41
|
+
"@dhis2/app-service-config": "3.18.0-beta.2",
|
|
42
|
+
"@dhis2/data-engine": "3.18.0-beta.2",
|
|
42
43
|
"react": "^16.8.6 || ^18",
|
|
43
44
|
"react-dom": "^16.8.6 || ^18"
|
|
44
45
|
}
|
package/.gitignore
DELETED
package/d2.config.js
DELETED
package/jest.config.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
collectCoverageFrom: [
|
|
3
|
-
'src/**/*.(js|jsx|ts|tsx)',
|
|
4
|
-
'!src/index.ts',
|
|
5
|
-
'!src/**/types/*',
|
|
6
|
-
'!src/**/types.ts',
|
|
7
|
-
'!src/engine/index.ts',
|
|
8
|
-
'!src/links/index.ts',
|
|
9
|
-
'!src/react/index.ts',
|
|
10
|
-
],
|
|
11
|
-
|
|
12
|
-
// Setup react-testing-library
|
|
13
|
-
setupFilesAfterEnv: ['<rootDir>/src/setupRTL.ts'],
|
|
14
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { render, waitFor } from '@testing-library/react'
|
|
2
|
-
import * as React from 'react'
|
|
3
|
-
import { CustomDataProvider, DataQuery } from '../react'
|
|
4
|
-
|
|
5
|
-
describe('<DataQuery />', () => {
|
|
6
|
-
it('should render without failing', async () => {
|
|
7
|
-
const data = {
|
|
8
|
-
answer: 42,
|
|
9
|
-
}
|
|
10
|
-
const wrapper = ({ children }: { children?: React.ReactNode }) => (
|
|
11
|
-
<CustomDataProvider data={data}>{children}</CustomDataProvider>
|
|
12
|
-
)
|
|
13
|
-
const renderFunction = jest.fn(() => null)
|
|
14
|
-
|
|
15
|
-
render(
|
|
16
|
-
<DataQuery query={{ answer: { resource: 'answer' } }}>
|
|
17
|
-
{renderFunction}
|
|
18
|
-
</DataQuery>,
|
|
19
|
-
{ wrapper }
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
expect(renderFunction).toHaveBeenCalledTimes(1)
|
|
23
|
-
expect(renderFunction).toHaveBeenLastCalledWith(
|
|
24
|
-
expect.objectContaining({
|
|
25
|
-
called: true,
|
|
26
|
-
loading: true,
|
|
27
|
-
})
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
await waitFor(() => {
|
|
31
|
-
expect(renderFunction).toHaveBeenCalledTimes(2)
|
|
32
|
-
expect(renderFunction).toHaveBeenLastCalledWith(
|
|
33
|
-
expect.objectContaining({
|
|
34
|
-
called: true,
|
|
35
|
-
loading: false,
|
|
36
|
-
data,
|
|
37
|
-
})
|
|
38
|
-
)
|
|
39
|
-
})
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('should render an error', async () => {
|
|
43
|
-
const expectedError = new Error('Something went wrong')
|
|
44
|
-
const data = {
|
|
45
|
-
test: () => {
|
|
46
|
-
throw expectedError
|
|
47
|
-
},
|
|
48
|
-
}
|
|
49
|
-
const wrapper = ({ children }: { children?: React.ReactNode }) => (
|
|
50
|
-
<CustomDataProvider data={data}>{children}</CustomDataProvider>
|
|
51
|
-
)
|
|
52
|
-
const renderFunction = jest.fn(() => null)
|
|
53
|
-
|
|
54
|
-
render(
|
|
55
|
-
<DataQuery query={{ test: { resource: 'test' } }}>
|
|
56
|
-
{renderFunction}
|
|
57
|
-
</DataQuery>,
|
|
58
|
-
{ wrapper }
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
expect(renderFunction).toHaveBeenCalledTimes(1)
|
|
62
|
-
expect(renderFunction).toHaveBeenLastCalledWith(
|
|
63
|
-
expect.objectContaining({
|
|
64
|
-
called: true,
|
|
65
|
-
loading: true,
|
|
66
|
-
})
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
await waitFor(() => {
|
|
70
|
-
expect(renderFunction).toHaveBeenCalledTimes(2)
|
|
71
|
-
expect(renderFunction).toHaveBeenLastCalledWith(
|
|
72
|
-
expect.objectContaining({
|
|
73
|
-
called: true,
|
|
74
|
-
loading: false,
|
|
75
|
-
error: expectedError,
|
|
76
|
-
})
|
|
77
|
-
)
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
})
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { render, waitFor, act } from '@testing-library/react'
|
|
2
|
-
import * as React from 'react'
|
|
3
|
-
import { CustomDataProvider, DataMutation } from '../react'
|
|
4
|
-
|
|
5
|
-
describe('<DataMutation />', () => {
|
|
6
|
-
it('should render without failing', async () => {
|
|
7
|
-
const endpointSpy = jest.fn(() => Promise.resolve(42))
|
|
8
|
-
const mutation = {
|
|
9
|
-
resource: 'answer',
|
|
10
|
-
type: 'create' as const,
|
|
11
|
-
data: {
|
|
12
|
-
question: '?',
|
|
13
|
-
},
|
|
14
|
-
}
|
|
15
|
-
const data = {
|
|
16
|
-
answer: endpointSpy,
|
|
17
|
-
}
|
|
18
|
-
const wrapper = ({ children }: { children?: React.ReactNode }) => (
|
|
19
|
-
<CustomDataProvider data={data}>{children}</CustomDataProvider>
|
|
20
|
-
)
|
|
21
|
-
const renderSpy: jest.Mock = jest.fn(() => null)
|
|
22
|
-
render(<DataMutation mutation={mutation}>{renderSpy}</DataMutation>, {
|
|
23
|
-
wrapper,
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
expect(endpointSpy).toHaveBeenCalledTimes(0)
|
|
27
|
-
expect(renderSpy).toHaveBeenCalledTimes(1)
|
|
28
|
-
expect(renderSpy).toHaveBeenLastCalledWith([
|
|
29
|
-
expect.any(Function),
|
|
30
|
-
expect.objectContaining({
|
|
31
|
-
called: false,
|
|
32
|
-
loading: false,
|
|
33
|
-
engine: expect.any(Object),
|
|
34
|
-
}),
|
|
35
|
-
])
|
|
36
|
-
|
|
37
|
-
await act(async () => {
|
|
38
|
-
const firstRenderSpyCall = renderSpy.mock.calls[0]
|
|
39
|
-
const firstRenderSpyArgument = firstRenderSpyCall[0]
|
|
40
|
-
const [mutate] = firstRenderSpyArgument
|
|
41
|
-
await mutate()
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
waitFor(() => {
|
|
45
|
-
expect(endpointSpy).toHaveBeenCalledTimes(1)
|
|
46
|
-
expect(renderSpy).toHaveBeenCalledTimes(2)
|
|
47
|
-
expect(renderSpy).toHaveBeenLastCalledWith([
|
|
48
|
-
expect.any(Function),
|
|
49
|
-
expect.objectContaining({
|
|
50
|
-
called: true,
|
|
51
|
-
loading: true,
|
|
52
|
-
engine: expect.any(Object),
|
|
53
|
-
}),
|
|
54
|
-
])
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
waitFor(() => {
|
|
58
|
-
expect(endpointSpy).toHaveBeenCalledTimes(1)
|
|
59
|
-
expect(renderSpy).toHaveBeenCalledTimes(3)
|
|
60
|
-
expect(renderSpy).toHaveBeenLastCalledWith([
|
|
61
|
-
expect.any(Function),
|
|
62
|
-
expect.objectContaining({
|
|
63
|
-
called: true,
|
|
64
|
-
loading: false,
|
|
65
|
-
data: { answer: 42 },
|
|
66
|
-
engine: expect.any(Object),
|
|
67
|
-
}),
|
|
68
|
-
])
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
|
-
})
|
package/src/index.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { DataEngine, CustomDataLink } from '@dhis2/data-engine'
|
|
2
|
-
import type { CustomData, CustomLinkOptions } from '@dhis2/data-engine'
|
|
3
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
4
|
-
import React from 'react'
|
|
5
|
-
import { DataContext } from '../context/DataContext'
|
|
6
|
-
import { queryClientOptions as queryClientDefaults } from './DataProvider'
|
|
7
|
-
|
|
8
|
-
interface CustomProviderInput {
|
|
9
|
-
children: React.ReactNode
|
|
10
|
-
data: CustomData
|
|
11
|
-
options?: CustomLinkOptions
|
|
12
|
-
queryClientOptions?: any
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const CustomDataProvider = ({
|
|
16
|
-
children,
|
|
17
|
-
data,
|
|
18
|
-
options,
|
|
19
|
-
queryClientOptions = queryClientDefaults,
|
|
20
|
-
}: CustomProviderInput): JSX.Element => {
|
|
21
|
-
const link = new CustomDataLink(data, options)
|
|
22
|
-
const engine = new DataEngine(link)
|
|
23
|
-
const queryClient = new QueryClient(queryClientOptions)
|
|
24
|
-
const context = { engine }
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<QueryClientProvider client={queryClient}>
|
|
28
|
-
<DataContext.Provider value={context}>
|
|
29
|
-
{children}
|
|
30
|
-
</DataContext.Provider>
|
|
31
|
-
</QueryClientProvider>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { Mutation, MutationOptions } from '@dhis2/data-engine'
|
|
2
|
-
import { MutationRenderInput } from '../../types'
|
|
3
|
-
import { useDataMutation } from '../hooks/useDataMutation'
|
|
4
|
-
|
|
5
|
-
interface MutationInput extends MutationOptions {
|
|
6
|
-
mutation: Mutation
|
|
7
|
-
children: (input: MutationRenderInput) => any
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const DataMutation = ({
|
|
11
|
-
mutation,
|
|
12
|
-
onComplete,
|
|
13
|
-
onError,
|
|
14
|
-
variables,
|
|
15
|
-
children,
|
|
16
|
-
}: MutationInput) => {
|
|
17
|
-
const mutationState = useDataMutation(mutation, {
|
|
18
|
-
onComplete,
|
|
19
|
-
onError,
|
|
20
|
-
variables,
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
return children(mutationState)
|
|
24
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { DataEngine, RestAPILink } from '@dhis2/data-engine'
|
|
2
|
-
import { render } from '@testing-library/react'
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import { DataContext } from '../context/DataContext'
|
|
5
|
-
import { DataProvider } from './DataProvider'
|
|
6
|
-
|
|
7
|
-
describe('DataProvider', () => {
|
|
8
|
-
it('Should pass a new engine and RestAPILink to consumers', () => {
|
|
9
|
-
const renderFunction = jest.fn()
|
|
10
|
-
render(
|
|
11
|
-
<DataProvider baseUrl="test" apiVersion={42}>
|
|
12
|
-
<DataContext.Consumer>{renderFunction}</DataContext.Consumer>
|
|
13
|
-
</DataProvider>
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
expect(renderFunction).toHaveBeenCalled()
|
|
17
|
-
const context = renderFunction.mock.calls.pop()[0]
|
|
18
|
-
expect(context).not.toBeUndefined()
|
|
19
|
-
expect(context.engine).toBeInstanceOf(DataEngine)
|
|
20
|
-
expect(context.engine.link).toBeInstanceOf(RestAPILink)
|
|
21
|
-
})
|
|
22
|
-
})
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/* eslint-disable react/no-unused-prop-types */
|
|
2
|
-
import { useConfig } from '@dhis2/app-service-config'
|
|
3
|
-
import { DataEngine, RestAPILink } from '@dhis2/data-engine'
|
|
4
|
-
import {
|
|
5
|
-
QueryClient,
|
|
6
|
-
QueryClientProvider,
|
|
7
|
-
type QueryClientConfig,
|
|
8
|
-
} from '@tanstack/react-query'
|
|
9
|
-
import React from 'react'
|
|
10
|
-
import { DataContext } from '../context/DataContext'
|
|
11
|
-
|
|
12
|
-
export interface ProviderInput {
|
|
13
|
-
baseUrl?: string
|
|
14
|
-
apiVersion?: number
|
|
15
|
-
children: React.ReactNode
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const queryClientOptions: QueryClientConfig = {
|
|
19
|
-
defaultOptions: {
|
|
20
|
-
queries: {
|
|
21
|
-
// Disable automatic error retries
|
|
22
|
-
retry: false,
|
|
23
|
-
// Retry on mount if query has errored
|
|
24
|
-
retryOnMount: true,
|
|
25
|
-
// Refetch on mount if data is stale
|
|
26
|
-
refetchOnMount: true,
|
|
27
|
-
// Don't refetch when the window regains focus
|
|
28
|
-
refetchOnWindowFocus: false,
|
|
29
|
-
// Don't refetch after connection issues
|
|
30
|
-
refetchOnReconnect: false,
|
|
31
|
-
// RQv4 uses 'online' as the default, which pauses queries without network connection.
|
|
32
|
-
// 'always' reestablishes behavior from v3, and lets requests fire when offline
|
|
33
|
-
// https://tanstack.com/query/latest/docs/framework/react/guides/network-mode
|
|
34
|
-
networkMode: 'always',
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const queryClient = new QueryClient(queryClientOptions)
|
|
40
|
-
|
|
41
|
-
export const DataProvider = (props: ProviderInput): JSX.Element => {
|
|
42
|
-
const config = {
|
|
43
|
-
...useConfig(),
|
|
44
|
-
...props,
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const link = new RestAPILink(config)
|
|
48
|
-
const engine = new DataEngine(link)
|
|
49
|
-
const context = { engine }
|
|
50
|
-
|
|
51
|
-
return (
|
|
52
|
-
<QueryClientProvider client={queryClient}>
|
|
53
|
-
<DataContext.Provider value={context}>
|
|
54
|
-
{props.children}
|
|
55
|
-
</DataContext.Provider>
|
|
56
|
-
</QueryClientProvider>
|
|
57
|
-
)
|
|
58
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { Query, QueryOptions } from '@dhis2/data-engine'
|
|
2
|
-
import { QueryRenderInput } from '../../types'
|
|
3
|
-
import { useDataQuery } from '../hooks/useDataQuery'
|
|
4
|
-
|
|
5
|
-
interface QueryInput extends QueryOptions {
|
|
6
|
-
query: Query
|
|
7
|
-
children: (input: QueryRenderInput) => any
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const DataQuery = ({
|
|
11
|
-
query,
|
|
12
|
-
onComplete,
|
|
13
|
-
onError,
|
|
14
|
-
variables,
|
|
15
|
-
lazy,
|
|
16
|
-
children,
|
|
17
|
-
}: QueryInput) => {
|
|
18
|
-
const queryState = useDataQuery(query, {
|
|
19
|
-
onComplete,
|
|
20
|
-
onError,
|
|
21
|
-
variables,
|
|
22
|
-
lazy,
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
return children(queryState)
|
|
26
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { defaultDataContext } from './defaultDataContext'
|
|
2
|
-
|
|
3
|
-
describe('defaultContext', () => {
|
|
4
|
-
const originalError = console.error
|
|
5
|
-
const mockError = jest.fn()
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
jest.clearAllMocks()
|
|
8
|
-
console.error = mockError
|
|
9
|
-
})
|
|
10
|
-
afterEach(() => (console.error = originalError))
|
|
11
|
-
|
|
12
|
-
it('Should throw if query is called', () => {
|
|
13
|
-
const context = defaultDataContext
|
|
14
|
-
expect(
|
|
15
|
-
context.engine.query({
|
|
16
|
-
test: {
|
|
17
|
-
resource: 'test',
|
|
18
|
-
},
|
|
19
|
-
})
|
|
20
|
-
).rejects.toBeTruthy()
|
|
21
|
-
|
|
22
|
-
expect(mockError).toHaveBeenCalledTimes(1)
|
|
23
|
-
expect(mockError.mock.calls.pop()).toMatchInlineSnapshot(`
|
|
24
|
-
Array [
|
|
25
|
-
"DHIS2 data context must be initialized, please ensure that you include a <DataProvider> in your application",
|
|
26
|
-
]
|
|
27
|
-
`)
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('Should throw and log if mutate is called', () => {
|
|
31
|
-
const context = defaultDataContext
|
|
32
|
-
expect(
|
|
33
|
-
context.engine.mutate({
|
|
34
|
-
type: 'create',
|
|
35
|
-
resource: 'test',
|
|
36
|
-
data: {},
|
|
37
|
-
})
|
|
38
|
-
).rejects.toBeTruthy()
|
|
39
|
-
expect(mockError).toHaveBeenCalled()
|
|
40
|
-
expect(mockError.mock.calls.pop()).toMatchInlineSnapshot(`
|
|
41
|
-
Array [
|
|
42
|
-
"DHIS2 data context must be initialized, please ensure that you include a <DataProvider> in your application",
|
|
43
|
-
]
|
|
44
|
-
`)
|
|
45
|
-
})
|
|
46
|
-
})
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { DataEngine, ErrorLink } from '@dhis2/data-engine'
|
|
2
|
-
|
|
3
|
-
const errorMessage =
|
|
4
|
-
'DHIS2 data context must be initialized, please ensure that you include a <DataProvider> in your application'
|
|
5
|
-
|
|
6
|
-
const link = new ErrorLink(errorMessage)
|
|
7
|
-
const engine = new DataEngine(link)
|
|
8
|
-
|
|
9
|
-
export const defaultDataContext = { engine }
|
package/src/react/hooks/index.ts
DELETED