-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: [DHIS2-16372] Delete Relationships #3520
Merged
eirikhaugstulen
merged 8 commits into
master
from
eh/feat/DHIS2-16372_DeleteTEIRelationships
Mar 3, 2024
Merged
Changes from 4 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
e0e8dde
feat: implement delete relationship
eirikhaugstulen 54f7653
fix: pr-review
eirikhaugstulen d4548f6
Merge remote-tracking branch 'origin/master' into eh/feat/DHIS2-16372…
eirikhaugstulen 8b0c9a9
fix: add handeApiResponse
eirikhaugstulen 170ef8f
Merge remote-tracking branch 'origin/master' into eh/feat/DHIS2-16372…
eirikhaugstulen 6303e1f
chore: merge-conflicts
eirikhaugstulen 3bd471d
chore: temp disable tests
eirikhaugstulen e42623c
Merge remote-tracking branch 'origin/master' into eh/feat/DHIS2-16372…
eirikhaugstulen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
146 changes: 146 additions & 0 deletions
146
cypress/e2e/WidgetsForEnrollmentPages/WidgetTeiRelationship/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import { When, Given, Then, Before } from '@badeball/cypress-cucumber-preprocessor'; | ||
|
||
Before({ tags: '@with-mocked-relationship-data' }, () => { | ||
cy.intercept({ | ||
method: 'GET', | ||
url: '**/tracker/relationships**', | ||
}, { | ||
statusCode: 200, | ||
body: { | ||
instances: [ | ||
{ | ||
relationship: 'mwswG3RMNuu', | ||
relationshipType: 'XdP5nraLPZ0', | ||
createdAt: '2024-02-01T11:50:25.479', | ||
from: { | ||
trackedEntity: { | ||
trackedEntity: 'EaOyKGOIGRp', | ||
trackedEntityType: 'nEenWmSyUEp', | ||
orgUnit: 'DiszpKrYNg8', | ||
attributes: [ | ||
{ | ||
attribute: 'cejWyOfXge6', | ||
displayName: 'Gender', | ||
createdAt: '2016-08-03T23:47:14.509', | ||
updatedAt: '2016-08-03T23:47:14.509', | ||
valueType: 'TEXT', | ||
value: 'Female', | ||
}, | ||
{ | ||
attribute: 'zDhUuAYrxNC', | ||
displayName: 'Last name', | ||
createdAt: '2016-08-03T23:47:14.517', | ||
updatedAt: '2016-08-03T23:47:14.517', | ||
valueType: 'TEXT', | ||
value: 'Jones', | ||
}, | ||
{ | ||
attribute: 'w75KJ2mc4zz', | ||
code: 'MMD_PER_NAM', | ||
displayName: 'First name', | ||
createdAt: '2016-08-03T23:47:14.516', | ||
updatedAt: '2016-08-03T23:47:14.516', | ||
valueType: 'TEXT', | ||
value: 'Anna', | ||
}, | ||
], | ||
}, | ||
}, | ||
to: { | ||
trackedEntity: { | ||
trackedEntity: 'G1NNqS1RDeO', | ||
trackedEntityType: 'nEenWmSyUEp', | ||
orgUnit: 'DiszpKrYNg8', | ||
attributes: [ | ||
{ | ||
attribute: 'w75KJ2mc4zz', | ||
code: 'MMD_PER_NAM', | ||
displayName: 'First name', | ||
createdAt: '2024-02-01T11:50:25.479', | ||
updatedAt: '2024-02-01T11:50:25.479', | ||
valueType: 'TEXT', | ||
value: 'John', | ||
}, | ||
{ | ||
attribute: 'lZGmxYbs97q', | ||
code: 'MMD_PER_ID', | ||
displayName: 'Unique ID', | ||
createdAt: '2024-02-01T11:50:25.476', | ||
updatedAt: '2024-02-01T11:50:25.476', | ||
valueType: 'TEXT', | ||
value: '0078200', | ||
}, | ||
{ | ||
attribute: 'zDhUuAYrxNC', | ||
displayName: 'Last name', | ||
createdAt: '2024-02-01T11:50:25.479', | ||
updatedAt: '2024-02-01T11:50:25.479', | ||
valueType: 'TEXT', | ||
value: 'Mayer', | ||
}, | ||
], | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
}).as('getRelationships'); | ||
}); | ||
|
||
Given('the user can see the relationship widget', () => { | ||
cy.get('[data-test="tracked-entity-relationship-widget"]') | ||
.should('be.visible'); | ||
}); | ||
|
||
When('there is an existing relationship', () => { | ||
cy.wait('@getRelationships'); | ||
cy.get('[data-test="tracked-entity-relationship-widget"]') | ||
.within(() => { | ||
cy.get('[data-test="relationship-table-row"]') | ||
.contains('John'); | ||
}); | ||
}); | ||
|
||
When('the user clicks the delete button', () => { | ||
cy.get('[data-test="tracked-entity-relationship-widget"]') | ||
.within(() => { | ||
cy.get('[data-test="relationship-table-row"]') | ||
.contains('John') | ||
.parent() | ||
.within(() => { | ||
cy.get('[data-test="delete-relationship-button"]') | ||
.click(); | ||
}); | ||
}); | ||
}); | ||
|
||
When('the user can see the delete relationship modal', () => { | ||
cy.get('[data-test="delete-relationship-modal"]').should('be.visible'); | ||
}); | ||
|
||
When('the user clicks the confirm delete button', () => { | ||
cy.intercept({ | ||
method: 'POST', | ||
url: '**/tracker?importStrategy=DELETE&async=false**', | ||
}).as('deleteRelationship'); | ||
|
||
cy.get('[data-test="delete-relationship-modal"]') | ||
.within(() => { | ||
cy.get('[data-test="delete-relationship-confirmation-button"]') | ||
.click(); | ||
}); | ||
|
||
cy.wait('@deleteRelationship') | ||
.its('request.body') | ||
.should('deep.equal', { | ||
relationships: [{ relationship: 'mwswG3RMNuu' }], | ||
}); | ||
}); | ||
|
||
Then('the user can see the relationship widget without the deleted relationship', () => { | ||
cy.get('[data-test="tracked-entity-relationship-widget"]') | ||
.within(() => { | ||
cy.get('[data-test="relationship-table-body"]') | ||
.should('not.exist'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
...s/WidgetsRelationship/common/RelationshipsWidget/DeleteRelationship/DeleteRelationship.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// @flow | ||
|
||
import React, { useState } from 'react'; | ||
import i18n from '@dhis2/d2-i18n'; | ||
import { | ||
DataTableCell, | ||
IconDelete16, | ||
Modal, | ||
ModalContent, | ||
ModalTitle, | ||
ModalActions, | ||
ButtonStrip, | ||
Button, | ||
colors, | ||
} from '@dhis2/ui'; | ||
import { IconButton } from 'capture-ui'; | ||
import { withStyles } from '@material-ui/core/styles'; | ||
|
||
type Props = { | ||
handleDeleteRelationship: () => void, | ||
disabled?: boolean, | ||
classes: { | ||
tableCell: string, | ||
}, | ||
} | ||
|
||
const styles = { | ||
tableCell: { | ||
display: 'flex', | ||
justifyContent: 'center', | ||
}, | ||
}; | ||
|
||
export const DeleteRelationshipPlain = ({ handleDeleteRelationship, disabled, classes }: Props) => { | ||
const [isModalOpen, setIsModalOpen] = useState(false); | ||
return ( | ||
<> | ||
<DataTableCell className={classes.tableCell}> | ||
<IconButton | ||
simonadomnisoru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
onClick={() => { | ||
if (disabled) return; | ||
setIsModalOpen(true); | ||
}} | ||
dataTest={'delete-relationship-button'} | ||
> | ||
<IconDelete16 color={colors.red600} /> | ||
</IconButton> | ||
</DataTableCell> | ||
|
||
{isModalOpen && ( | ||
<Modal | ||
hide={!isModalOpen} | ||
onClose={() => setIsModalOpen(false)} | ||
dataTest={'delete-relationship-modal'} | ||
> | ||
<ModalTitle>{i18n.t('Delete relationship')}</ModalTitle> | ||
<ModalContent> | ||
{i18n.t('Deleting the relationship is permanent and cannot be undone. Are you sure you want to delete this relationship?')} | ||
</ModalContent> | ||
|
||
<ModalActions> | ||
<ButtonStrip> | ||
<Button onClick={() => setIsModalOpen(false)}> | ||
{i18n.t('No, cancel')} | ||
</Button> | ||
|
||
<Button | ||
destructive | ||
dataTest={'delete-relationship-confirmation-button'} | ||
onClick={() => { | ||
handleDeleteRelationship(); | ||
setIsModalOpen(false); | ||
}} | ||
> | ||
{i18n.t('Yes, delete relationship')} | ||
</Button> | ||
</ButtonStrip> | ||
</ModalActions> | ||
</Modal> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export const DeleteRelationship = withStyles(styles)(DeleteRelationshipPlain); |
3 changes: 3 additions & 0 deletions
3
...ore/components/WidgetsRelationship/common/RelationshipsWidget/DeleteRelationship/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// @flow | ||
|
||
export { DeleteRelationship } from './DeleteRelationship'; |
69 changes: 69 additions & 0 deletions
69
...idgetsRelationship/common/RelationshipsWidget/DeleteRelationship/useDeleteRelationship.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// @flow | ||
import i18n from '@dhis2/d2-i18n'; | ||
import log from 'loglevel'; | ||
import { errorCreator, FEATURES, useFeature } from 'capture-core-utils'; | ||
import { handleAPIResponse, REQUESTED_ENTITIES } from 'capture-core/utils/api'; | ||
import { useMutation, useQueryClient } from 'react-query'; | ||
import { useAlert, useDataEngine } from '@dhis2/app-runtime'; | ||
import { ReactQueryAppNamespace } from '../../../../../utils/reactQueryHelpers'; | ||
|
||
type Props = { | ||
sourceId: string, | ||
}; | ||
|
||
export type OnDeleteRelationship = ({ relationshipId: string }) => void; | ||
|
||
const deleteRelationshipMutation = { | ||
resource: 'tracker?importStrategy=DELETE&async=false', | ||
type: 'create', | ||
data: ({ relationshipId }) => ({ | ||
relationships: [ | ||
{ | ||
relationship: relationshipId, | ||
}, | ||
], | ||
}), | ||
}; | ||
export const useDeleteRelationship = ({ sourceId }: Props): { onDeleteRelationship: OnDeleteRelationship } => { | ||
const dataEngine = useDataEngine(); | ||
const queryKey: string = useFeature(FEATURES.exportablePayload) ? 'relationships' : 'instances'; | ||
const queryClient = useQueryClient(); | ||
const { show: showError } = useAlert( | ||
i18n.t('An error occurred while deleting the relationship.'), | ||
{ | ||
critical: true, | ||
}, | ||
); | ||
const { mutate: onDeleteRelationship } = useMutation( | ||
({ relationshipId }) => dataEngine.mutate(deleteRelationshipMutation, { variables: { relationshipId } }), | ||
{ | ||
onMutate: ({ relationshipId }) => { | ||
const prevRelationships = queryClient | ||
.getQueryData([ReactQueryAppNamespace, 'relationships', sourceId]); | ||
|
||
const apiRelationships = handleAPIResponse(REQUESTED_ENTITIES.relationships, prevRelationships); | ||
|
||
const newRelationships = apiRelationships | ||
?.filter(({ relationship }) => relationship !== relationshipId); | ||
|
||
queryClient.setQueryData( | ||
[ReactQueryAppNamespace, 'relationships', sourceId], | ||
{ [queryKey]: newRelationships }); | ||
|
||
return { prevRelationships }; | ||
}, | ||
onError: (error, { relationshipId }, context) => { | ||
log.error(errorCreator('An error occurred while deleting the relationship')({ error, relationshipId })); | ||
showError(); | ||
|
||
if (!context?.prevRelationships) return; | ||
queryClient.setQueryData( | ||
[ReactQueryAppNamespace, 'relationships', sourceId], | ||
context.prevRelationships, | ||
); | ||
}, | ||
}, | ||
); | ||
|
||
return { onDeleteRelationship }; | ||
}; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reminder after the bug in the API is fixed: To keep DB consistency, the relationship that was deleted should be added back. Otherwise, the
there is an existing relationship
step will fail the next time the scenario is run.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This data is actually just mocked, so no need to add it again. We intercept the request for relationships in the before hook and then assert that we send the actual call to the api with the correct data! WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing against mocked data versus the API and DB has advantages and disadvantages 🤔 . By using mock data, we will no longer benefit from having the latest API changes available. We will need to ensure that the mocks are always up to date, otherwise, we may not detect when we introduce bugs.
Let's discuss it more in-depth in our next FE check-in meeting!