Skip to content

Commit

Permalink
refactor: convert d2 to app runtime (#1294)
Browse files Browse the repository at this point in the history
Remove schema fetching as with dataEngine we don't need d2 models
anymore.

Convert CRUD operations to use dataEngine instead of d2.

The TranslationsDialog expects a d2 model representing the object on
which apply translations.
If the object passed has the href attribute, the dialog does not try to
access the api endpoint via the modelDefinition.
Simulate a model by adding the modelDefinition
object and make the dialog api requests work by making sure the object has the href property.

Co-authored-by: Jen Jones Arnesen <jennifer@dhis2.org>
  • Loading branch information
edoardo and jenniferarnesen authored Jan 6, 2021
1 parent 5bbf13c commit 5825008
Show file tree
Hide file tree
Showing 32 changed files with 340 additions and 165 deletions.
2 changes: 2 additions & 0 deletions .prettierignore → .d2styleignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/node_modules/*
/i18n/*
/public/*
/src/locales/*
/cypress/assets
/cypress/fixtures
3 changes: 0 additions & 3 deletions .eslintignore

This file was deleted.

2 changes: 1 addition & 1 deletion cypress/integration/common/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ Then('the {string} dashboard displays in view mode', title => {
})

cy.get(dashboardTitleSel).should('be.visible').and('contain', title)
cy.get(chartSel).should('exist')
cy.get(chartSel, EXTENDED_TIMEOUT).should('exist')
})
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"private": true,
"license": "BSD-3-Clause",
"dependencies": {
"@dhis2/analytics": "^11.3.4",
"@dhis2/analytics": "^12.0.2",
"@dhis2/app-runtime": "^2.6.1",
"@dhis2/app-runtime-adapter-d2": "^1.0.1",
"@dhis2/d2-i18n": "^1.0.6",
Expand Down
7 changes: 5 additions & 2 deletions src/AppWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'
import dhis2theme from '@dhis2/d2-ui-core/theme/mui3.theme'
import { Provider as ReduxProvider } from 'react-redux'
import { D2Shim } from '@dhis2/app-runtime-adapter-d2'
import { useDataEngine } from '@dhis2/app-runtime'

import App from './components/App'
import configureStore from './configureStore'
Expand Down Expand Up @@ -34,16 +35,18 @@ if (authorization) {
}

const AppWrapper = () => {
const dataEngine = useDataEngine()

return (
<ReduxProvider store={configureStore()}>
<ReduxProvider store={configureStore(dataEngine)}>
<MuiThemeProvider theme={muiTheme()}>
<D2Shim d2Config={d2Config} i18nRoot="./i18n">
{({ d2 }) => {
if (!d2) {
// TODO: Handle errors in d2 initialization
return null
}
return <App d2={d2} />
return <App d2={d2} dataEngine={dataEngine} />
}}
</D2Shim>
</MuiThemeProvider>
Expand Down
8 changes: 7 additions & 1 deletion src/AppWrapper.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import React from 'react'
import ReactDOM from 'react-dom'
import AppWrapper from './AppWrapper'

jest.mock('./components/App', () => () => <div />)
jest.mock(
'./components/App',
() =>
function MockApp() {
return <div />
}
)
jest.mock('@dhis2/app-runtime-adapter-d2', () => {
return {
D2Shim: ({ children }) => children({ d2: {} }),
Expand Down
26 changes: 19 additions & 7 deletions src/actions/dashboards.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,13 @@ export const acSetDashboardItems = value => ({

// thunks

export const tFetchDashboards = () => async dispatch => {
const collection = await apiFetchDashboards()
dispatch(acSetDashboards(collection.toArray()))
export const tFetchDashboards = () => async (
dispatch,
getState,
dataEngine
) => {
const dashboards = await apiFetchDashboards(dataEngine)
dispatch(acSetDashboards(dashboards))
}

export const tSelectDashboard = id => async (dispatch, getState) => {
Expand Down Expand Up @@ -88,7 +92,11 @@ export const tSelectDashboard = id => async (dispatch, getState) => {
}
}

export const tStarDashboard = (id, isStarred) => async dispatch => {
export const tStarDashboard = (id, isStarred) => async (
dispatch,
getState,
dataEngine
) => {
const onSuccess = id => {
dispatch(acSetDashboardStarred(id, isStarred))
return id
Expand All @@ -99,16 +107,20 @@ export const tStarDashboard = (id, isStarred) => async dispatch => {
return error
}
try {
await apiStarDashboard(id, isStarred)
await apiStarDashboard(dataEngine, id, isStarred)
return onSuccess(id)
} catch (err) {
return onError(err)
}
}

export const tDeleteDashboard = id => async dispatch => {
export const tDeleteDashboard = id => async (
dispatch,
getState,
dataEngine
) => {
try {
await apiDeleteDashboard(id)
await apiDeleteDashboard(dataEngine, id)
await dispatch(tFetchDashboards())

return Promise.resolve()
Expand Down
4 changes: 2 additions & 2 deletions src/actions/dimensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const acSetDimensions = dimensions => ({
value: dimensions,
})

export const tSetDimensions = d2 => async (dispatch, getState) => {
export const tSetDimensions = () => async (dispatch, getState, dataEngine) => {
const onSuccess = dimensions => {
dispatch(acSetDimensions(dimensions))
}
Expand All @@ -19,7 +19,7 @@ export const tSetDimensions = d2 => async (dispatch, getState) => {

try {
const displayNameProp = sGetSettingsDisplayNameProperty(getState())
const dimensions = await apiFetchDimensions(d2, displayNameProp)
const dimensions = await apiFetchDimensions(dataEngine, displayNameProp)

// filter out CATEGORY that are not of type ATTRIBUTE
const filteredDimensions = dimensions.filter(
Expand Down
6 changes: 3 additions & 3 deletions src/actions/editDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const acRemoveDashboardItem = value => ({

// thunks

export const tSaveDashboard = () => async (dispatch, getState) => {
export const tSaveDashboard = () => async (dispatch, getState, dataEngine) => {
const dashboard = sGetEditDashboardRoot(getState())

const dashboardToSave = {
Expand All @@ -131,8 +131,8 @@ export const tSaveDashboard = () => async (dispatch, getState) => {

try {
const dashboardId = dashboardToSave.id
? await updateDashboard(dashboardToSave)
: await postDashboard(dashboardToSave)
? await updateDashboard(dataEngine, dashboardToSave)
: await postDashboard(dataEngine, dashboardToSave)

dispatch(acClearEditDashboard())
await dispatch(tSetSelectedDashboardById(dashboardId))
Expand Down
10 changes: 7 additions & 3 deletions src/actions/selected.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ export const acClearSelectedItemActiveTypes = () => ({
})

// thunks
export const tSetSelectedDashboardById = id => async (dispatch, getState) => {
export const tSetSelectedDashboardById = id => async (
dispatch,
getState,
dataEngine
) => {
dispatch(acSetSelectedIsLoading(true))

const alertTimeout = setTimeout(() => {
Expand Down Expand Up @@ -107,7 +111,7 @@ export const tSetSelectedDashboardById = id => async (dispatch, getState) => {
dispatch(acAddVisualization(getVisualizationFromItem(item)))
break
case MESSAGES:
dispatch(tGetMessages(id))
dispatch(tGetMessages(dataEngine))
break
default:
break
Expand All @@ -131,7 +135,7 @@ export const tSetSelectedDashboardById = id => async (dispatch, getState) => {
}

try {
const dashboard = await apiFetchDashboard(id)
const dashboard = await apiFetchDashboard(dataEngine, id)

return onSuccess(dashboard)
} catch (err) {
Expand Down
124 changes: 84 additions & 40 deletions src/api/dashboards.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,99 @@
import { getInstance } from 'd2'
import arrayClean from 'd2-utilizr/lib/arrayClean'
import { onError, getDashboardFields } from './index'

// Get "all" dashboards on startup
export const apiFetchDashboards = () =>
getInstance()
.then(d2 =>
d2.models.dashboard.list({
fields: [
getDashboardFields().join(','),
'dashboardItems[id]',
].join(','),
paging: 'false',
export const dashboardsQuery = {
resource: 'dashboards',
params: {
fields: [getDashboardFields(), 'dashboardItems[id]'].join(','),
paging: false,
},
}

export const dashboardQuery = {
resource: 'dashboards',
id: ({ id }) => id,
params: {
fields: arrayClean(
getDashboardFields({
withItems: true,
withFavorite: { withDimensions: false },
})
)
.catch(onError)
).join(','),
},
}

export const starDashboardMutation = {
type: 'create',
resource: 'dashboards',
// TODO create mutation does not accept the id function
// we must use a workaround until dataEngine supports dynamic resource/path
id: ({ id }) => `${id}/favorite`,
}

export const unstarDashboardMutation = {
type: 'delete',
resource: 'dashboards',
id: ({ id }) => `${id}/favorite`,
}

export const deleteDashboardMutation = {
type: 'delete',
resource: 'dashboards',
id: ({ id }) => id,
}

// Get "all" dashboards on startup
export const apiFetchDashboards = async dataEngine => {
try {
const dashboardsData = await dataEngine.query({
dashboards: dashboardsQuery,
})

return dashboardsData.dashboards.dashboards
} catch (error) {
onError(error)
}
}

// Get more info about selected dashboard
export const apiFetchDashboard = id =>
getInstance()
.then(d2 =>
d2.models.dashboard.get(id, {
fields: arrayClean(
getDashboardFields({
withItems: true,
withFavorite: { withDimensions: false },
})
).join(','),
})
export const apiFetchDashboard = async (dataEngine, id) => {
try {
const dashboardData = await dataEngine.query(
{ dashboard: dashboardQuery },
{
variables: {
id,
},
}
)
.catch(onError)

// Star dashboard
export const apiStarDashboard = (id, isStarred) => {
const url = `dashboards/${id}/favorite`
return dashboardData.dashboard
} catch (error) {
onError(error)
}
}

getInstance().then(d2 => {
// Star dashboard
export const apiStarDashboard = async (dataEngine, id, isStarred) => {
try {
if (isStarred) {
d2.Api.getApi().post(url)
await dataEngine.mutate(starDashboardMutation, {
variables: { id },
})
} else {
d2.Api.getApi().delete(url)
await dataEngine.mutate(unstarDashboardMutation, {
variables: { id },
})
}
})
} catch (error) {
onError(error)
}
}

export const apiDeleteDashboard = id => {
return getInstance()
.then(d2 => {
return d2.models.dashboards
.get(id)
.then(dashboard => dashboard.delete())
})
.catch(onError)
export const apiDeleteDashboard = async (dataEngine, id) => {
try {
await dataEngine.mutate(deleteDashboardMutation, { variables: { id } })
} catch (error) {
onError(error)
}
}
Loading

0 comments on commit 5825008

Please sign in to comment.