Skip to content

Commit

Permalink
Merge branch 'dev' into wx-1734-workflow-permissions-modal-kp
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-schu authored Oct 4, 2024
2 parents 3b8a585 + 3e1f96a commit bc7987d
Show file tree
Hide file tree
Showing 40 changed files with 440 additions and 377 deletions.
156 changes: 78 additions & 78 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
6 changes: 3 additions & 3 deletions integration-tests/tests/run-analysis-azure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const {
getAnimatedDrawer,
input,
noSpinnersAfter,
waitForNoModal,
waitForNoModalDrawer,
waitForNoSpinners,
} = require('../utils/integration-utils');
const { registerTest } = require('../utils/jest-utils');
Expand Down Expand Up @@ -51,7 +51,7 @@ const testRunAnalysisAzure = _.flowRight(
timeout: Millis.ofMinute,
});
await click(page, clickable({ textContains: 'Close' }), { timeout: Millis.ofMinute });
await waitForNoModal(page);
await waitForNoModalDrawer(page);

// Navigate to analysis launcher
await click(page, clickable({ textContains: `${notebookName}.ipynb` }));
Expand All @@ -63,7 +63,7 @@ const testRunAnalysisAzure = _.flowRight(
await click(page, clickable({ textContains: 'Open' }));
await findText(page, 'Azure Cloud Environment');
await click(page, clickable({ textContains: 'Create' }));
await waitForNoModal(page);
await waitForNoModalDrawer(page);

// Wait for env to begin creating
await findElement(page, clickable({ textContains: 'JupyterLab Environment' }));
Expand Down
6 changes: 3 additions & 3 deletions integration-tests/tests/run-analysis.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const {
getAnimatedDrawer,
input,
noSpinnersAfter,
waitForNoModal,
waitForNoModalDrawer,
waitForNoSpinners,
} = require('../utils/integration-utils');
const { registerTest } = require('../utils/jest-utils');
Expand Down Expand Up @@ -47,7 +47,7 @@ const testRunAnalysisFn = _.flowRight(
timeout: Millis.ofMinute,
});
await click(page, clickable({ textContains: 'Close' }), { timeout: Millis.ofMinute });
await waitForNoModal(page);
await waitForNoModalDrawer(page);

// Navigate to analysis launcher
await click(page, clickable({ textContains: `${notebookName}.ipynb` }));
Expand All @@ -61,7 +61,7 @@ const testRunAnalysisFn = _.flowRight(
});
await findText(page, 'Jupyter Cloud Environment');
await click(page, clickable({ text: 'Create' }));
await waitForNoModal(page);
await waitForNoModalDrawer(page);

// Wait for env to begin creating
await findElement(page, clickable({ textContains: 'Jupyter Environment' }), { timeout: Millis.ofSeconds(40) });
Expand Down
6 changes: 3 additions & 3 deletions integration-tests/tests/run-rstudio.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const {
getAnimatedDrawer,
input,
noSpinnersAfter,
waitForNoModal,
waitForNoModalDrawer,
waitForNoSpinners,
} = require('../utils/integration-utils');
const { registerTest } = require('../utils/jest-utils');
Expand Down Expand Up @@ -50,7 +50,7 @@ const testRunRStudioFn = _.flowRight(
timeout: Millis.ofMinute,
});
await click(page, clickable({ textContains: 'Close' }), { timeout: Millis.ofMinute });
await waitForNoModal(page);
await waitForNoModalDrawer(page);

// Navigate to analysis launcher
await click(page, clickable({ textContains: `${rFileName}.Rmd` }));
Expand All @@ -63,7 +63,7 @@ const testRunRStudioFn = _.flowRight(
action: () => click(page, clickable({ textContains: 'Open' })),
});
await click(page, clickable({ text: 'Create' }));
await waitForNoModal(page);
await waitForNoModalDrawer(page);

// Wait for env to begin creating
await findElement(page, clickable({ textContains: 'RStudio Environment' }), { timeout: Millis.ofMinutes(2) });
Expand Down
7 changes: 7 additions & 0 deletions integration-tests/utils/integration-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,12 @@ const waitForModal = (page, { timeout = 30000 } = {}) => {
return page.waitForSelector('.ReactModal__Overlay', { hidden: false, timeout });
};

const waitForNoModalDrawer = async (page) => {
await waitForNoModal(page);
// Matches the animation transition time
await delay(200);
};

// Puppeteer works by internally using MutationObserver. We are setting up the listener before
// the action to ensure that the spinner rendering is captured by the observer, followed by
// waiting for the spinner to be removed
Expand Down Expand Up @@ -631,6 +637,7 @@ module.exports = {
waitForNoModal,
waitForMenu,
waitForModal,
waitForNoModalDrawer,
waitForNoSpinners,
withPageLogging,
withScreenshot,
Expand Down
6 changes: 6 additions & 0 deletions src/libs/ajax/billing/Billing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
GoogleBillingAccount,
SpendReport,
} from 'src/libs/ajax/billing/billing-models';
import { SupportSummary } from 'src/support/SupportResourceType';

export const Billing = (signal?: AbortSignal) => ({
listProjects: async (): Promise<BillingProject[]> => {
Expand All @@ -25,6 +26,11 @@ export const Billing = (signal?: AbortSignal) => ({
return res.json();
},

adminGetProject: async (projectName: string): Promise<SupportSummary> => {
const res = await fetchRawls(`admin/billing/${projectName}`, _.merge(authOpts(), { signal }));
return res.json();
},

listAccounts: async (): Promise<GoogleBillingAccount[]> => {
const res = await fetchRawls('user/billingAccounts?firecloudHasAccess=true', _.merge(authOpts(), { signal }));
return res.json();
Expand Down
6 changes: 6 additions & 0 deletions src/libs/ajax/workspaces/Workspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from 'src/libs/ajax/workspaces/workspace-models';
import { getTerraUser } from 'src/libs/state';
import * as Utils from 'src/libs/utils';
import { SupportSummary } from 'src/support/SupportResourceType';

const getSnapshotEntityMetadata = Utils.memoizeAsync(
async (
Expand Down Expand Up @@ -95,6 +96,11 @@ export const Workspaces = (signal?: AbortSignal) => ({
return response.json();
},

adminGetById: async (workspaceId: string): Promise<SupportSummary> => {
const res = await fetchRawls(`admin/workspaces/${workspaceId}`, _.merge(authOpts(), { signal }));
return res.json();
},

workspaceV2: (namespace: string, name: string) => {
const root = `workspaces/v2/${namespace}/${name}`;

Expand Down
11 changes: 10 additions & 1 deletion src/libs/ajax/workspaces/workspace-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ export interface MethodConfiguration {
}

// TYPES RELATED TO WORKSPACE SETTINGS
export type WorkspaceSetting = BucketLifecycleSetting | SoftDeleteSetting | RequesterPaysSetting;
export type WorkspaceSetting =
| BucketLifecycleSetting
| SoftDeleteSetting
| RequesterPaysSetting
| SeparateSubmissionFinalOutputsSetting;

export interface BucketLifecycleSetting {
settingType: 'GcpBucketLifecycle';
Expand All @@ -49,6 +53,11 @@ export interface RequesterPaysSetting {
config: { enabled: boolean };
}

export interface SeparateSubmissionFinalOutputsSetting {
settingType: 'SeparateSubmissionFinalOutputs';
config: { enabled: boolean };
}

export interface BucketLifecycleRule {
action: {
actionType: string;
Expand Down
22 changes: 7 additions & 15 deletions src/pages/workspaces/workspace/workflows/WorkflowView.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { act, fireEvent, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { h } from 'react-hyperscript-helpers';
import { Ajax } from 'src/libs/ajax';
import { Apps } from 'src/libs/ajax/leonardo/Apps';
import { leoDiskProvider } from 'src/libs/ajax/leonardo/providers/LeoDiskProvider';
import { Runtimes } from 'src/libs/ajax/leonardo/Runtimes';
import { Methods } from 'src/libs/ajax/methods/Methods';
import { Workspaces } from 'src/libs/ajax/workspaces/Workspaces';
import { getLocalPref, setLocalPref } from 'src/libs/prefs';
Expand All @@ -16,6 +18,8 @@ jest.mock('src/libs/ajax');

jest.mock('src/libs/ajax/Dockstore');
jest.mock('src/libs/ajax/GoogleStorage');
jest.mock('src/libs/ajax/leonardo/Apps');
jest.mock('src/libs/ajax/leonardo/Runtimes');
jest.mock('src/libs/ajax/methods/Methods');
jest.mock('src/libs/ajax/Metrics');
jest.mock('src/libs/ajax/workspaces/Workspaces');
Expand Down Expand Up @@ -247,8 +251,6 @@ describe('Workflow View (GCP)', () => {
const mockLaunchResponse = jest.fn(() => Promise.resolve({ submissionId: 'abc123', ...initializedGoogleWorkspace.workspaceId }));

const mockDefaultAjax = () => {
asMockedFn(leoDiskProvider.list).mockImplementation(jest.fn());

Methods.mockReturnValue({
list: jest.fn(() => Promise.resolve(methodList)),
method: () => ({
Expand Down Expand Up @@ -284,19 +286,9 @@ describe('Workflow View (GCP)', () => {
}),
}),
});
Ajax.mockImplementation(() => ({
Disks: {
disksV1: () => ({
list: jest.fn(),
}),
},
Runtimes: {
listV2: jest.fn(),
},
Apps: {
list: jest.fn().mockReturnValue([]),
},
}));
Apps.mockReturnValue({ list: jest.fn().mockReturnValue([]) });
Runtimes.mockReturnValue({ listV2: jest.fn() });
asMockedFn(leoDiskProvider.list).mockImplementation(jest.fn());
};

it('view workflow in workspace from mock import', async () => {
Expand Down
12 changes: 10 additions & 2 deletions src/support/SupportResourceType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,16 @@ export const supportResources: SupportResourceType[] = [
resourceType: 'managed-group',
loadSupportSummaryFn: (id: FullyQualifiedResourceId) => Ajax().Groups.group(id.resourceId).getSupportSummary(),
},
{ displayName: 'Workspace', resourceType: 'workspace', loadSupportSummaryFn: undefined },
{ displayName: 'Billing Project', resourceType: 'billing-project', loadSupportSummaryFn: undefined },
{
displayName: 'Workspace',
resourceType: 'workspace',
loadSupportSummaryFn: (id: FullyQualifiedResourceId) => Ajax().Workspaces.adminGetById(id.resourceId),
},
{
displayName: 'Billing Project',
resourceType: 'billing-project',
loadSupportSummaryFn: (id: FullyQualifiedResourceId) => Ajax().Billing.adminGetProject(id.resourceId),
},
{ displayName: 'Dataset', resourceType: 'dataset', loadSupportSummaryFn: undefined },
{ displayName: 'Snapshot', resourceType: 'datasnapshot', loadSupportSummaryFn: undefined },
].sort((a, b) => a.displayName.localeCompare(b.displayName));
23 changes: 22 additions & 1 deletion src/workspaces/SettingsModal/BucketLifecycleSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { CreatableSelect, ExternalLink, useUniqueId } from '@terra-ui-packages/components';
import { CreatableSelect, ExternalLink, Icon, useUniqueId } from '@terra-ui-packages/components';
import _ from 'lodash/fp';
import React, { ReactNode } from 'react';
import { NumberInput } from 'src/components/input';
import colors from 'src/libs/colors';
import * as Style from 'src/libs/style';
import Setting from 'src/workspaces/SettingsModal/Setting';
import { suggestedPrefixes } from 'src/workspaces/SettingsModal/utils';

Expand Down Expand Up @@ -59,6 +61,25 @@ const BucketLifecycleSettings = (props: BucketLifecycleSettingsProps): ReactNode
}
>
<div style={{ marginTop: '.5rem', marginBottom: '.5rem' }}>
<div style={{ ...Style.elements.noticeContainer }}>
<div
style={{
display: 'grid',
gridTemplateColumns: 'auto auto',
fontStyle: 'italic',
}}
>
<Icon
icon='warning-standard'
size={24}
style={{ color: colors.warning(), flex: 'none', marginRight: '0.5rem' }}
/>
<div style={{ flex: 1 }}>
Enabling lifecycle rules will also change the directory structure for future workflow submissions by
separating files into submission/intermediates and submissions/final-outputs directories.
</div>
</div>
</div>
<div style={{ marginTop: '.75rem', marginBottom: '.5rem' }}>Delete objects in:</div>
<CreatableSelect
isClearable
Expand Down
29 changes: 26 additions & 3 deletions src/workspaces/SettingsModal/SettingsModal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { axe } from 'jest-axe';
import _ from 'lodash/fp';
import React from 'react';
import { Ajax } from 'src/libs/ajax';
import { SeparateSubmissionFinalOutputsSetting } from 'src/libs/ajax/workspaces/workspace-models';
import Events, { extractWorkspaceDetails } from 'src/libs/events';
import { isFeaturePreviewEnabled } from 'src/libs/feature-previews';
import { GCP_BUCKET_LIFECYCLE_RULES } from 'src/libs/feature-previews-config';
Expand Down Expand Up @@ -113,6 +114,16 @@ describe('SettingsModal', () => {
config: { enabled: false },
};

const separateSubmissionOutputsDisabledSetting: SeparateSubmissionFinalOutputsSetting = {
settingType: 'SeparateSubmissionFinalOutputs',
config: { enabled: false },
};

const separateSubmissionOutputsEnabledSetting: SeparateSubmissionFinalOutputsSetting = {
settingType: 'SeparateSubmissionFinalOutputs',
config: { enabled: true },
};

const setup = (currentSetting: WorkspaceSetting[], updateSettingsMock: jest.Mock<any, any>) => {
jest.resetAllMocks();
jest.spyOn(console, 'log').mockImplementation(() => {});
Expand Down Expand Up @@ -174,7 +185,7 @@ describe('SettingsModal', () => {
// Assert
expect(onDismiss).toHaveBeenCalled();
expect(captureEvent).not.toHaveBeenCalled();
// On save we do persist the default soft delete setting so it now explicit.
// On save we do persist the default soft delete setting so it is now explicit.
expect(updateSettingsMock).toHaveBeenCalledWith([defaultSoftDeleteSetting]);
});

Expand Down Expand Up @@ -276,7 +287,11 @@ describe('SettingsModal', () => {
expect(prefixInput.getSelectedOptions()).toEqual([suggestedPrefixes.allObjects]);
expect(prefixInput.inputElement).not.toHaveAttribute('disabled');
const allOptions = await prefixInput.getOptions();
expect(allOptions).toEqual([suggestedPrefixes.submissions, suggestedPrefixes.submissionIntermediaries]);
expect(allOptions).toEqual([
suggestedPrefixes.submissionIntermediaries,
suggestedPrefixes.submissionFinalOutputs,
suggestedPrefixes.submissions,
]);

const daysInput = getDays();
expect(daysInput).toHaveValue(4);
Expand Down Expand Up @@ -366,7 +381,11 @@ describe('SettingsModal', () => {
await user.click(screen.getByRole('button', { name: 'Save' }));

// Assert
expect(updateSettingsMock).toHaveBeenCalledWith([defaultSoftDeleteSetting, noLifecycleRules]);
expect(updateSettingsMock).toHaveBeenCalledWith([
defaultSoftDeleteSetting,
separateSubmissionOutputsDisabledSetting,
noLifecycleRules,
]);
expect(captureEvent).toHaveBeenCalledWith(Events.workspaceSettingsBucketLifecycle, {
enabled: false,
prefix: null,
Expand Down Expand Up @@ -401,6 +420,7 @@ describe('SettingsModal', () => {
// Assert
expect(updateSettingsMock).toHaveBeenCalledWith([
defaultSoftDeleteSetting,
separateSubmissionOutputsDisabledSetting,
{
config: {
rules: [
Expand Down Expand Up @@ -447,6 +467,7 @@ describe('SettingsModal', () => {
// Assert
expect(updateSettingsMock).toHaveBeenCalledWith([
defaultSoftDeleteSetting,
separateSubmissionOutputsEnabledSetting,
{
config: {
rules: [
Expand Down Expand Up @@ -501,6 +522,7 @@ describe('SettingsModal', () => {
// Assert
expect(updateSettingsMock).toHaveBeenCalledWith([
defaultSoftDeleteSetting,
separateSubmissionOutputsEnabledSetting,
{
config: {
rules: [
Expand Down Expand Up @@ -558,6 +580,7 @@ describe('SettingsModal', () => {
// Assert
expect(updateSettingsMock).toHaveBeenCalledWith([
defaultSoftDeleteSetting,
separateSubmissionOutputsEnabledSetting,
{
config: {
rules: [
Expand Down
2 changes: 1 addition & 1 deletion src/workspaces/SettingsModal/SettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ const SettingsModal = (props: SettingsModalProps): ReactNode => {
<Modal
title='Configure Workspace Settings'
onDismiss={props.onDismiss}
width={550}
width={600}
okButton={
<ButtonPrimary disabled={!!getSaveTooltip()} onClick={persistSettings} tooltip={getSaveTooltip()}>
Save
Expand Down
Loading

0 comments on commit bc7987d

Please sign in to comment.