-
Notifications
You must be signed in to change notification settings - Fork 21
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
[WM-2427] Remove ability for Cromwell appTypes to be deleted #4592
Changes from 10 commits
01837c7
f21115f
f94f52e
cc9aa48
4268574
d553483
b50e128
8dabb33
dcdc6a4
fcb1fbc
5e09b16
f870837
6d16f23
5d35d62
58937b1
268eae7
fcf557a
9814dee
27fead3
8d14b12
0b70825
967a632
2466b5a
30e520a
c6b70e1
b68ef1b
60641fc
9125f5b
b3988d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,10 +3,13 @@ import { ReactNode } from 'react'; | |
import { h } from 'react-hyperscript-helpers'; | ||
import { isResourceDeletable } from 'src/analysis/utils/resource-utils'; | ||
import { getDisplayRuntimeStatus } from 'src/analysis/utils/runtime-utils'; | ||
import { cromwellAppToolLabels } from 'src/analysis/utils/tool-utils'; | ||
import { Link } from 'src/components/common'; | ||
import { makeMenuIcon } from 'src/components/PopupTrigger'; | ||
import { isApp } from 'src/libs/ajax/leonardo/models/app-models'; | ||
import { App, isApp } from 'src/libs/ajax/leonardo/models/app-models'; | ||
import { LeoRuntimeStatus } from 'src/libs/ajax/leonardo/models/runtime-models'; | ||
import * as Utils from 'src/libs/utils'; | ||
import { makeCromwellAppsNotDeletable } from 'src/pages/EnvironmentsPage/environmentsPermissions'; | ||
|
||
import { DecoratedComputeResource } from './Environments.models'; | ||
|
||
|
@@ -18,19 +21,28 @@ export interface DeleteButtonProps { | |
export const DeleteButton = (props: DeleteButtonProps): ReactNode => { | ||
const { resource, onClick } = props; | ||
const resourceType = isApp(resource) ? 'app' : 'runtime'; | ||
const isDeletable = isResourceDeletable(resourceType, resource); | ||
// Casting resource to type App to access the appType property | ||
const castToApp = resource as App; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't need to cast. It sounds like we want to function
Bonus points if you can consolidate it with the Sorry if this is a bit confusing, definitely down to pair and help you get this through. You've got all the pieces in place here though, a great second iteration! tl;dr, we should not need to cast in the DeleteButton, the permissions provider should handle the guts of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I see what you're saying. Let me take a stab at this implementation, and I'll reach out if I get stuck. Thank you for all of the feedback! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, just a quick clarification, are you advocating for calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I left that intentionally vague 😅 . I think the I think we are encountering the need for another provider, one that can tell you whether a resource is in a state (business logic or otherwise) that allows a specific action. In the scope of your PR, I could see you doing any of the following, as long as we don't straddle between multiple approaches:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry if I'm a bit scattered here, but trying to move towards an ideal architecture with old code through incremental improvements is a bit tricky 😅 . I'd vote for the third above, but I understand if you would rather go with something along the lines of #2 to not expand the scope of your PR too much There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No worries! I don't think it'll be too much of a lift to create a new wrapper provider, so I'll do my best to try and implement option #3 here. I'll reach out in Slack if things start to get a little wonky in my understanding. |
||
const appsToDelete = makeCromwellAppsNotDeletable(); | ||
const isDeletable = appsToDelete.canBeDeleted(castToApp) && isResourceDeletable(resourceType, resource); | ||
|
||
return h( | ||
Link, | ||
{ | ||
disabled: !isDeletable, | ||
tooltip: isDeletable | ||
? 'Delete cloud environment' | ||
: `Cannot delete a cloud environment while in status ${_.upperCase( | ||
getDisplayRuntimeStatus(resource.status as LeoRuntimeStatus) | ||
)}.`, | ||
tooltip: Utils.cond( | ||
[isDeletable, () => 'Delete cloud environment'], | ||
[Object.keys(cromwellAppToolLabels).includes(castToApp.appType), () => 'Deleting not yet supported'], | ||
[ | ||
Utils.DEFAULT, | ||
() => | ||
`Cannot delete a cloud environment while in status ${_.upperCase( | ||
getDisplayRuntimeStatus(resource.status as LeoRuntimeStatus) | ||
)}.`, | ||
] | ||
), | ||
onClick: () => onClick(resource), | ||
}, | ||
[makeMenuIcon('trash'), 'Delete'] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the moment, nothing. TS was unhappy here because it expected a |
||
[makeMenuIcon('trash', undefined), 'Delete'] | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,5 +22,9 @@ export interface LeoResourcePermissionsProvider { | |
canPauseResource: (resource: App | ListRuntimeItem) => boolean; | ||
} | ||
|
||
export interface CanDeleteProvider { | ||
canBeDeleted: (resource: App) => boolean; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this can live in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd also call it |
||
} | ||
|
||
export type DeleteRuntimeProvider = Pick<LeoRuntimeProvider, 'delete'>; | ||
export type DeleteDiskProvider = Pick<LeoDiskProvider, 'delete'>; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
import { NavLinkProvider } from '@terra-ui-packages/core-utils'; | ||
import { act, fireEvent, getAllByRole, screen } from '@testing-library/react'; | ||
import { act, fireEvent, getAllByRole, screen, within } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import _ from 'lodash/fp'; | ||
import { h } from 'react-hyperscript-helpers'; | ||
|
@@ -524,6 +524,51 @@ describe('Environments', () => { | |
expect(getTextContentForColumn(fourthAppRow, 8)).toBe(makeCompleteDate(azureApp2.auditInfo.dateAccessed)); | ||
}); | ||
|
||
it('Renders Cromwell apps with disabled delete', async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for adding a detailed test! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed! this is a great looking test. |
||
// Arrange | ||
const props = getEnvironmentsProps(); | ||
|
||
const googleApp1 = generateTestAppWithGoogleWorkspace({}, defaultGoogleWorkspace); | ||
const azureApp1 = generateTestAppWithAzureWorkspace({ appType: appToolLabels.CROMWELL }, defaultAzureWorkspace); | ||
const azureWorkspace2 = generateAzureWorkspace(); | ||
const azureApp2 = generateTestAppWithAzureWorkspace({ appType: appToolLabels.WORKFLOWS_APP }, azureWorkspace2); | ||
asMockedFn(props.leoAppData.listWithoutProject).mockResolvedValue([googleApp1, azureApp1, azureApp2]); | ||
asMockedFn(props.useWorkspaces).mockReturnValue({ | ||
...defaultUseWorkspacesProps, | ||
workspaces: [defaultGoogleWorkspace, defaultAzureWorkspace, azureWorkspace2], | ||
}); | ||
|
||
// Act | ||
await act(async () => { | ||
render(h(Environments, props)); | ||
}); | ||
|
||
// Assert | ||
const tableRows: HTMLElement[] = screen.getAllByRole('row').slice(1); // skip header row | ||
kpierre13 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const firstAppRow: HTMLElement = tableRows[0]; | ||
const actionColumnButton1 = within(firstAppRow) | ||
.getAllByRole('button') | ||
.filter((button) => button.textContent.includes('Delete'))[0]; | ||
expect(actionColumnButton1).not.toHaveAttribute('disabled'); | ||
|
||
const secondAppRow: HTMLElement = tableRows[1]; | ||
const actionColumnButton2 = within(secondAppRow) | ||
.getAllByRole('button') | ||
.filter((button) => button.textContent.includes('Delete'))[0]; | ||
expect(actionColumnButton2).toHaveAttribute('disabled'); | ||
|
||
const thirdAppRow: HTMLElement = tableRows[2]; | ||
const actionColumnButton3 = within(thirdAppRow) | ||
.getAllByRole('button') | ||
.filter((button) => button.textContent.includes('Delete'))[0]; | ||
expect(actionColumnButton3).toHaveAttribute('disabled'); | ||
|
||
// Check for tooltip, and that it's only present for azure apps | ||
const notSupportedTooltip = screen.getAllByText('Deleting not yet supported'); | ||
expect(notSupportedTooltip).toBeInTheDocument; | ||
expect(notSupportedTooltip.length).toBe(2); | ||
}); | ||
|
||
it.each([ | ||
{ app: generateTestAppWithGoogleWorkspace({}, defaultGoogleWorkspace), workspace: defaultGoogleWorkspace }, | ||
{ app: generateTestAppWithAzureWorkspace({}, defaultAzureWorkspace), workspace: defaultAzureWorkspace }, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import { LeoResourcePermissionsProvider } from 'src/analysis/Environments/Environments.models'; | ||
import { CanDeleteProvider, LeoResourcePermissionsProvider } from 'src/analysis/Environments/Environments.models'; | ||
import { getCreatorForCompute } from 'src/analysis/utils/resource-utils'; | ||
import { cromwellAppToolLabels } from 'src/analysis/utils/tool-utils'; | ||
import { App } from 'src/libs/ajax/leonardo/models/app-models'; | ||
import { PersistentDisk } from 'src/libs/ajax/leonardo/models/disk-models'; | ||
import { ListRuntimeItem } from 'src/libs/ajax/leonardo/models/runtime-models'; | ||
|
@@ -21,6 +22,14 @@ const makePermissionsProvider = (userEmailGetter: () => string): LeoResourcePerm | |
return permissionsProvider; | ||
}; | ||
|
||
export const makeCromwellAppsNotDeletable = (): CanDeleteProvider => { | ||
return { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should move to the above |
||
canBeDeleted: (resource: App) => { | ||
return !Object.keys(cromwellAppToolLabels).includes(resource.appType); | ||
}, | ||
}; | ||
}; | ||
|
||
const currentUserEmailGetter = (): string => { | ||
return getTerraUser().email!; | ||
}; | ||
|
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 can be deleted