Skip to content

Commit

Permalink
feat: Close Harvest when BI connection is removed in webview
Browse files Browse the repository at this point in the history
  • Loading branch information
doubleface authored and doubleface committed Oct 28, 2022
1 parent a105cd3 commit 45d9e20
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 13 deletions.
Expand Up @@ -117,4 +117,38 @@ describe('BIContractActivationWindow', () => {
expect.anything()
)
})
it('should show account delete dialog after BI connection removed and close harvest', async () => {
prepareOAuth.mockImplementation(() => ({ oAuthUrl: 'https://test.url' }))
isFlagshipApp.mockImplementation(() => false)
fetchExtraOAuthUrlParams.mockResolvedValue({})
const { getByRole } = setup()
await act(async () => {
await waitFor(() => {
return expect(getByRole('button').getAttribute('class')).not.toContain(
'Mui-disabled'
)
})
})

await act(async () => {
fireEvent.click(getByRole('button'))
})

checkOAuthData.mockImplementation(() => true)
await act(async () => {
sendMessageFn({
data: {
oAuthStateKey: 'statekey',
finalLocation: 'connection_deleted=true'
}
})
await waitFor(() =>
expect(
getByRole('button', { name: 'Close dialog' })
).toBeInTheDocument()
)
fireEvent.click(getByRole('button', { name: 'Close dialog' }))
})
expect(onAccountDeleted).toHaveBeenCalled()
})
})
Expand Up @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'
import { useClient } from 'cozy-client'
import Button from 'cozy-ui/transpiled/react/MuiCozyTheme/Buttons'
import ListItem from 'cozy-ui/transpiled/react/MuiCozyTheme/ListItem'
import { Dialog } from 'cozy-ui/transpiled/react/CozyDialogs'
import { findKonnectorPolicy } from '../../../konnector-policies'
import OAuthWindow from '../../OAuthWindow'
import withLocales from '../../hoc/withLocales'
Expand All @@ -18,18 +19,48 @@ const BIContractActivationWindow = ({
account,
t,
intentsApi,
innerAccountModalOverrides
innerAccountModalOverrides,
onAccountDeleted
}) => {
const [extraParams, setExtraParams] = useState(null)
const [isWindowVisible, setWindowVisible] = useState(false)
const [isDeleteConnectionDialogVisible, setDeleteConnectionDialogVisible] =
useState(false)
const [shouldRefreshContracts, setShouldRefreshContracts] = useState(false)

const konnectorPolicy = findKonnectorPolicy(konnector)
const client = useClient()

const onPopupClosed = () => {
/**
* Detects if a BI connection has been removed
*
* @param {String} finalLocation - url search param string from the final oauth location
* @returns {Boolean}
*/
const isBIConnectionRemoved = finalLocation => {
const queryParams = new URLSearchParams(finalLocation)
return queryParams.get('connection_deleted') === 'true'
}

/**
* @typedef FinalOAuthRealtimeMessage
* @property {String} finalLocation - url search param string from the final oauth location
* @property {String|null} err - OAuth error message
* @property {String} oAuthStateKey - OAuth key
*/

/**
*
* @param {String} key - OAuth key
* @param {FinalOAuthRealtimeMessage} oauthData
*/
const onPopupClosed = (key, oauthData) => {
setWindowVisible(false)
setShouldRefreshContracts(true)
if (oauthData && isBIConnectionRemoved(oauthData.finalLocation)) {
setDeleteConnectionDialogVisible(true)
} else {
setShouldRefreshContracts(true)
}
}

useEffect(() => {
Expand Down Expand Up @@ -72,6 +103,24 @@ const BIContractActivationWindow = ({
{t('contracts.handle-synchronization')}
</Button>
</ButtonWrapper>
<Dialog
open={isDeleteConnectionDialogVisible}
title={t('modal.deleteBIConnection.title')}
content={t('modal.deleteAccount.description')}
onClose={onAccountDeleted}
actions={
<>
<Button
variant="text"
color="primary"
aria-label="Close dialog"
onClick={onAccountDeleted}
>
{t('close')}
</Button>
</>
}
/>
{isWindowVisible && (
<OAuthWindow
extraParams={extraParams}
Expand All @@ -91,7 +140,9 @@ BIContractActivationWindow.propTypes = {
t: PropTypes.func,
account: PropTypes.object,
intentsApi: intentsApiProptype,
innerAccountModalOverrides: innerAccountModalOverridesProptype
innerAccountModalOverrides: innerAccountModalOverridesProptype,
/** What to do when the current account is deleted */
onAccountDeleted: PropTypes.func
}

// use isEqual to avoid an infinite rerender since the konnector object is a new one on each render
Expand Down
Expand Up @@ -41,7 +41,8 @@ const DumbContracts = ({
account,
konnector,
intentsApi,
innerAccountModalOverrides
innerAccountModalOverrides,
onAccountDeleted
}) => {
const { t } = useI18n()
const contractData = contracts.data ? contracts.data : contracts
Expand Down Expand Up @@ -77,6 +78,7 @@ const DumbContracts = ({
account={account}
intentsApi={intentsApi}
innerAccountModalOverrides={innerAccountModalOverrides}
onAccountDeleted={onAccountDeleted}
/>
)}
</NavigationListSection>
Expand All @@ -97,7 +99,9 @@ DumbContracts.propTypes = {
/** Can be present if showing contracts still linked to an account/konnector/trigger */
konnector: PropTypes.object,
intentsApi: intentsApiProptype,
innerAccountModalOverrides: innerAccountModalOverridesProptype
innerAccountModalOverrides: innerAccountModalOverridesProptype,
/** What to do when the current account is deleted */
onAccountDeleted: PropTypes.func
}

export const ContractsForAccount = compose(
Expand Down
Expand Up @@ -208,6 +208,7 @@ const ConfigurationTab = ({
account={account}
intentsApi={intentsApi}
innerAccountModalOverrides={innerAccountModalOverrides}
onAccountDeleted={onAccountDeleted}
/>
</NavigationList>
{showNewAccountButton ? (
Expand Down
3 changes: 2 additions & 1 deletion packages/cozy-harvest-lib/src/components/OAuthWindow.jsx
@@ -1,3 +1,4 @@
// @ts-check
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'

Expand Down Expand Up @@ -112,7 +113,7 @@ export class OAuthWindow extends PureComponent {
this.setState({ succeed: true })

if (typeof onSuccess !== 'function') return
onSuccess(data.key)
onSuccess(data.key, data)
}

/**
Expand Down
10 changes: 5 additions & 5 deletions packages/cozy-harvest-lib/src/helpers/oauth.js
Expand Up @@ -71,9 +71,8 @@ export const handleOAuthResponse = (options = {}) => {
// eslint-disable-next-line no-redeclare
/* global URLSearchParams */
const queryParams = new URLSearchParams(window.location.search)

const accountId = queryParams.get('account')
const errorMsg = queryParams.get('error')
const key = queryParams.get('account')
const error = queryParams.get('error')

/**
* Key for localStorage, used at the beginning of the OAuth process to store
Expand All @@ -84,8 +83,9 @@ export const handleOAuthResponse = (options = {}) => {
if (!oAuthStateKey) return false

realtime.sendNotification('io.cozy.accounts', OAUTH_REALTIME_CHANNEL, {
key: accountId,
error: errorMsg,
finalLocation: queryParams.toString(),
key,
error,
oAuthStateKey
})

Expand Down
5 changes: 4 additions & 1 deletion packages/cozy-harvest-lib/src/helpers/oauth.spec.js
Expand Up @@ -132,6 +132,8 @@ describe('Oauth helper', () => {
it('should send message with query string data', () => {
const expectedOAuthData = {
error: null,
finalLocation:
'account=bc2aca6566cf4a72afe6c615aa1e3d31&state=70720eb0-6204-484d',
key: 'bc2aca6566cf4a72afe6c615aa1e3d31',
oAuthStateKey: '70720eb0-6204-484d'
}
Expand All @@ -151,7 +153,8 @@ describe('Oauth helper', () => {
const expectedOAuthData = {
error: 'dismissed',
key: null,
oAuthStateKey: '70720eb0-6204-484d'
oAuthStateKey: '70720eb0-6204-484d',
finalLocation: 'error=dismissed&state=70720eb0-6204-484d'
}

handleOAuthResponse({ realtime })
Expand Down
4 changes: 4 additions & 0 deletions packages/cozy-harvest-lib/src/locales/en.json
Expand Up @@ -381,6 +381,10 @@
"title": "Connect to %{name}",
"button": "Add an account"
},
"deleteBIConnection": {
"title": "Bank disconnect",
"description": "Your request has been recorded. It will be effective in a short moment. Previously imported data will not be deleted."
},
"deleteAccount": {
"title": "Disconnection",
"description": "Your account will be disconnected, but already imported data will be kept.",
Expand Down
4 changes: 4 additions & 0 deletions packages/cozy-harvest-lib/src/locales/fr.json
Expand Up @@ -381,6 +381,10 @@
"title": "Connexion à %{name}",
"button": "Ajouter un compte"
},
"deleteBIConnection": {
"title": "Déconnexion de votre banque",
"description": "La déconnexion de votre banque a bien été prise en compte et sera effective dans quelques instants. Les données précédemment importées seront conservées."
},
"deleteAccount": {
"title": "Déconnexion",
"description": "Vous serez déconnecté de ce compte, mais les données déjà importées seront conservées.",
Expand Down

0 comments on commit 45d9e20

Please sign in to comment.