From 7d8dd5ee004bb1b50f482773ec625bbd3cd09320 Mon Sep 17 00:00:00 2001 From: sniedzielski <52816247+sniedzielski@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:08:07 +0200 Subject: [PATCH] CM-962: added pulling data from API - proof of concept (#92) --- src/actions.js | 53 ++++++++++++++++++++ src/pages/ImportDataApiPage.js | 90 +++++++++++++++++++++++++--------- src/reducer.js | 57 +++++++++++++++++++++ src/translations/en.json | 7 ++- 4 files changed, 184 insertions(+), 23 deletions(-) diff --git a/src/actions.js b/src/actions.js index ed1c7a7..fae3240 100644 --- a/src/actions.js +++ b/src/actions.js @@ -110,6 +110,22 @@ const UPLOAD_HISTORY_FULL_PROJECTION = () => [ 'userCreated {username}', ]; +const API_ETL_PROJECTION = () => [ + 'etlServices{nameOfService}', +]; + +export function fetchApiEtlServices() { + const payload = formatQuery( + 'etlServicesByServiceName', + [], + API_ETL_PROJECTION(), + ); + return graphql( + payload, + 'API_ETL_SERVICES', + ); +} + export function fetchIndividualEnrollmentSummary(params) { const payload = formatQuery( 'individualEnrollmentSummary', @@ -545,6 +561,43 @@ export function resolveTask(task, clientMutationLabel, user, approveOrFail, addi ); } +export function confirmPullingDataFromApiEtl(nameOfService, clientMutationLabel) { + // eslint-disable-next-line max-len + const mutationInput = `nameOfService: "${nameOfService}"`; + const mutation = formatMutation('etlServiceMutation', mutationInput, clientMutationLabel); + const requestedDateTime = new Date(); + return graphql( + mutation.payload, + [REQUEST(ACTION_TYPE.MUTATION), SUCCESS(ACTION_TYPE.PULL_API_DATA), ERROR(ACTION_TYPE.MUTATION)], + { + actionType: ACTION_TYPE.PULL_API_DATA, + clientMutationId: mutation.clientMutationId, + clientMutationLabel, + requestedDateTime, + }, + ); +} + +export function fetchMutationByLabel(clientMutationLabel) { + const MUTATION_RECEIVED_STATUS = 0; + const payload = formatPageQuery( + 'mutationLogs', + [`clientMutationLabel: "${clientMutationLabel}", status: ${MUTATION_RECEIVED_STATUS}`], + [ + 'id', + 'status', + 'error', + 'clientMutationId', + 'clientMutationLabel', + 'clientMutationDetails', + 'requestDateTime', + 'jsonExt', + 'autogeneratedCode', + ], + ); + return graphql(payload, ACTION_TYPE.FETCH_ACTIVE_MUTATIONS); +} + export function downloadGroups(params) { const payload = ` { diff --git a/src/pages/ImportDataApiPage.js b/src/pages/ImportDataApiPage.js index 1539607..8b775c4 100644 --- a/src/pages/ImportDataApiPage.js +++ b/src/pages/ImportDataApiPage.js @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import { connect, useDispatch, useSelector } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; import { makeStyles } from '@material-ui/styles'; @@ -22,7 +22,14 @@ import { clearConfirm, journalize, Helmet, + ProgressOrError, } from '@openimis/fe-core'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import { confirmPullingDataFromApiEtl, fetchApiEtlServices, fetchMutationByLabel } from '../actions'; const useStyles = makeStyles((theme) => ({ page: theme.page, @@ -39,27 +46,46 @@ const useStyles = makeStyles((theme) => ({ const API_WORKFLOW_HEADERS = [ 'ImportPageAPI.apiSelection', - 'ImportPageAPI.workflowName', 'ImportPageAPI.triggerImport', ]; -const workflows = [ - // Dummy data for workflows, replace with real data - { apiSelection: 'API 1', workflowName: 'Workflow 1' }, -]; - -const handleImportClick = (workflow) => { - // Implement the import functionality here - // eslint-disable-next-line no-console - console.log(`Triggering import for ${workflow.workflowName}`); -}; - // eslint-disable-next-line no-empty-pattern function ImportDataApiPage({ + confirmPullingDataFromApiEtl, + mutations, }) { + const dispatch = useDispatch(); const modulesManager = useModulesManager(); const classes = useStyles(); const { formatMessage } = useTranslations('individual', modulesManager); + const { + fetchingApiEtlServices, apiEtlServices, errorApiEtlServices, + } = useSelector((store) => store.individual); + const [openConfirmDialog, setOpenConfirmDialog] = useState(false); + const [serviceToPullData, setServiceToPullData] = useState(null); + + const confirmPullingData = (etlService) => { + setServiceToPullData(etlService); + setOpenConfirmDialog(true); + }; + + const handlePullingData = () => { + confirmPullingDataFromApiEtl( + serviceToPullData, + formatMessage('ImportPageAPI.confirmPullingData'), + ); + setOpenConfirmDialog(false); + setServiceToPullData(null); + }; + + useEffect(() => { + dispatch(fetchApiEtlServices()); + dispatch(fetchMutationByLabel(formatMessage('ImportPageAPI.confirmPullingData'))); + }, []); + + useEffect(() => { + dispatch(fetchMutationByLabel(formatMessage('ImportPageAPI.confirmPullingData'))); + }, [serviceToPullData]); return (
@@ -77,20 +103,19 @@ function ImportDataApiPage({ - {workflows.map((workflow) => ( - - - {workflow.apiSelection} - + + {apiEtlServices.map((etlService) => ( + - {workflow.workflowName} + {etlService.nameOfService} @@ -101,6 +126,25 @@ function ImportDataApiPage({ + setOpenConfirmDialog(false)} + > + {formatMessage('ImportPageAPI.confirmPullingData.title')} + + + {formatMessage('ImportPageAPI.confirmPullingData.message')} + + + + + + +
); @@ -110,13 +154,15 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ coreConfirm, clearConfirm, journalize, + confirmPullingDataFromApiEtl, }, dispatch); // eslint-disable-next-line no-unused-vars const mapStateToProps = (state, props) => ({ rights: state.core?.user?.i_user?.rights ?? [], confirmed: state.core.confirmed, - submittingMutation: state.payroll.submittingMutation, + submittingMutation: state.individual.submittingMutation, + mutations: state.individual.mutations, }); export default connect(mapStateToProps, mapDispatchToProps)(ImportDataApiPage); diff --git a/src/reducer.js b/src/reducer.js index 46dc1d3..a6bea29 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -11,6 +11,7 @@ import { pageInfo, decodeId, } from '@openimis/fe-core'; +import _ from 'lodash'; import { REQUEST, SUCCESS, ERROR, CLEAR, SET, } from './util/action-type'; @@ -47,6 +48,9 @@ export const ACTION_TYPE = { CONFIRM_GROUP_ENROLLMENT: 'CONFIRM_GROUP_ENROLLMENT', GET_PENDING_GROUPS_UPLOAD: 'GET_PENDING_GROUPS_UPLOAD', RESOLVE_TASK: 'TASK_MANAGEMENT_RESOLVE_TASK', + API_ETL_SERVICES: 'API_ETL_SERVICES', + PULL_API_DATA: 'PULL_API_DATA', + FETCH_ACTIVE_MUTATIONS: 'FETCH_ACTIVE_MUTATIONS', }; function reducer( @@ -140,6 +144,14 @@ function reducer( fetchedPendingGroups: false, errorPendingGroups: null, pendingGroupsPageInfo: {}, + + fetchingApiEtlServices: false, + fetchedApiEtlServices: false, + apiEtlServices: [], + errorApiEtlServices: null, + + fetchingMutations: false, + mutations: [], }, action, ) { @@ -638,6 +650,49 @@ function reducer( fetchingGroupIndividualHistory: false, errorGroupIndividualHistory: formatServerError(action.payload), }; + case REQUEST(ACTION_TYPE.API_ETL_SERVICES): + return { + ...state, + fetchingApiEtlServices: true, + fetchedApiEtlServices: false, + apiEtlServices: [], + errorApiEtlServices: null, + }; + case SUCCESS(ACTION_TYPE.API_ETL_SERVICES): + return { + ...state, + fetchingApiEtlServices: false, + fetchedApiEtlServices: true, + apiEtlServices: action.payload.data.etlServicesByServiceName + ? action.payload.data.etlServicesByServiceName.etlServices : [], + errorApiEtlServices: formatGraphQLError(action.payload), + }; + case ERROR(ACTION_TYPE.API_ETL_SERVICES): + return { + ...state, + fetchingApiEtlServices: false, + errorApiEtlServices: formatServerError(action.payload), + }; + case REQUEST(ACTION_TYPE.FETCH_ACTIVE_MUTATIONS): + return { + ...state, + fetchingMutations: true, + }; + case SUCCESS(ACTION_TYPE.FETCH_ACTIVE_MUTATIONS): { + const mutations = parseData(action.payload.data.mutationLogs); + const MUTATION_RECEIVED_STATUS = 0; + const activeMutations = mutations.filter((mutation) => mutation.status === MUTATION_RECEIVED_STATUS); + return { + ...state, + fetchingMutations: false, + mutations: _.unionBy(activeMutations, state.mutations, 'clientMutationId'), + }; + } + case ERROR(ACTION_TYPE.FETCH_ACTIVE_MUTATIONS): + return { + ...state, + fetchingMutations: false, + }; case REQUEST(ACTION_TYPE.MUTATION): return dispatchMutationReq(state, action); case ERROR(ACTION_TYPE.MUTATION): @@ -664,6 +719,8 @@ function reducer( return dispatchMutationResp(state, 'createGroupAndMoveIndividual', action); case SUCCESS(ACTION_TYPE.RESOLVE_TASK): return dispatchMutationResp(state, 'resolveTask', action); + case SUCCESS(ACTION_TYPE.PULL_API_DATA): + return dispatchMutationResp(state, 'etlServiceMutation', action); default: return state; } diff --git a/src/translations/en.json b/src/translations/en.json index 8252fe0..9da93c0 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -286,6 +286,11 @@ "ImportPage": "Import Data - API", "apiSelection": "Selected API", "workflowName": "Workflow Name", - "triggerImport": "Pull data" + "triggerImport": "Pull data", + "confirmPullingData": "Pulling Data from API", + "confirmPullingData.title": "Pulling Data from API", + "confirmPullingData.message": "Are you sure you want to confirm pulling data from selected API service?", + "confirmPullingData.cancel": "Cancel", + "confirmPullingData.confirm": "Confirm" } } \ No newline at end of file