From b5cdab34109bda2c34ec6f9851ce2814433790ed Mon Sep 17 00:00:00 2001 From: Miguel Silva Date: Fri, 31 Jan 2025 10:44:08 -0500 Subject: [PATCH] UIE-204 Narrow Ajax Usage pt24 - narrow Ajax() usage within last of src/libs/ajax area modules to call Ajax().SubAreaX directly. - demoted Ajax() to no longer be exported from ajax.ts. It is now called AjaxTestingRoot and only a setupAjaxTestUtil method is exposed. - setupAjaxTestUtil is called from appLoader.js since it is no longer incedentally initalized through ajax.ts module load. - unit tests added for setupAjaxTestUtil. - improve mock types where possible --- src/appLoader.js | 3 + src/libs/ajax.test.ts | 44 ++++++++++ src/libs/ajax.ts | 60 ++----------- src/libs/ajax/AzureStorage.ts | 4 +- src/libs/ajax/SamResources.ts | 3 +- .../ComputeImageProvider.ts | 6 +- .../WdsDataTableProvider.test.ts | 79 ++++++++--------- .../WdsDataTableProvider.ts | 16 ++-- .../GCSFileBrowserProvider.test.ts | 88 ++++++++----------- .../GCSFileBrowserProvider.ts | 19 ++-- src/libs/ajax/workspaces/Workspaces.ts | 2 +- src/support/ResourcePolicies.test.tsx | 2 +- src/workflows-app/RunDetails.test.ts | 3 +- 13 files changed, 154 insertions(+), 175 deletions(-) create mode 100644 src/libs/ajax.test.ts diff --git a/src/appLoader.js b/src/appLoader.js index c7a44b03a5..e1eff3ec37 100644 --- a/src/appLoader.js +++ b/src/appLoader.js @@ -12,6 +12,7 @@ import { initAuthTesting } from 'src/auth/app-load/init-auth-test'; import { initializeAuthMetrics } from 'src/auth/app-load/init-metrics'; import { initializeClientId } from 'src/auth/app-load/initializeClientId'; import { initializeSystemProperties } from 'src/auth/system-loader'; +import { setupAjaxTestUtil } from 'src/libs/ajax'; import { isAxeEnabled } from 'src/libs/config'; import Main from 'src/pages/Main'; @@ -21,6 +22,8 @@ RModal.defaultStyles = { overlay: {}, content: {} }; window._ = _; +setupAjaxTestUtil(); + initializeAuthListeners(); initializeAuthMetrics(); initAuthTesting(); diff --git a/src/libs/ajax.test.ts b/src/libs/ajax.test.ts new file mode 100644 index 0000000000..f8b22b3386 --- /dev/null +++ b/src/libs/ajax.test.ts @@ -0,0 +1,44 @@ +import { asMockedFn, partial } from '@terra-ui-packages/test-utils'; + +import { AjaxTestingContract, setupAjaxTestUtil } from './ajax'; +import { Apps, AppsAjaxContract } from './ajax/leonardo/Apps'; +import { Runtimes, RuntimesAjaxContract } from './ajax/leonardo/Runtimes'; +import { Workspaces, WorkspacesAjaxContract } from './ajax/workspaces/Workspaces'; + +jest.mock('src/libs/ajax/leonardo/Apps'); +jest.mock('src/libs/ajax/leonardo/Runtimes'); +jest.mock('src/libs/ajax/workspaces/Workspaces'); + +describe('setupAjaxTestUtil', () => { + beforeEach(() => { + asMockedFn(Apps).mockReturnValue(partial({})); + asMockedFn(Runtimes).mockReturnValue(partial({})); + asMockedFn(Workspaces).mockReturnValue(partial({})); + }); + + it('sets up Ajax data-call testing root', () => { + // Act + setupAjaxTestUtil(); + const ajaxTestingContract = (window as any).Ajax() as AjaxTestingContract; + + // Assert + expect(ajaxTestingContract).toBeDefined(); + expect(ajaxTestingContract.Apps).toBeDefined(); + expect(ajaxTestingContract.Runtimes).toBeDefined(); + expect(ajaxTestingContract.Workspaces).toBeDefined(); + }); + it('passes along signal arg to sub-areas', () => { + // Arrange + const signal = new AbortController().signal; + + // Act + setupAjaxTestUtil(); + const ajaxTestingContract = (window as any).Ajax(signal) as AjaxTestingContract; + + // Assert + expect(ajaxTestingContract).toBeDefined(); + expect(Apps).toBeCalledWith(signal); + expect(Runtimes).toBeCalledWith(signal); + expect(Workspaces).toBeCalledWith(signal); + }); +}); diff --git a/src/libs/ajax.ts b/src/libs/ajax.ts index e141ab8518..6630b33c60 100644 --- a/src/libs/ajax.ts +++ b/src/libs/ajax.ts @@ -1,66 +1,18 @@ -import { AzureStorage } from 'src/libs/ajax/AzureStorage'; -import { Billing } from 'src/libs/ajax/billing/Billing'; -import { Catalog } from 'src/libs/ajax/Catalog'; -import { DataRepo } from 'src/libs/ajax/DataRepo'; -import { Dockstore } from 'src/libs/ajax/Dockstore'; -import { DrsUriResolver } from 'src/libs/ajax/drs/DrsUriResolver'; -import { ExternalCredentials } from 'src/libs/ajax/ExternalCredentials'; -import { FirecloudBucket } from 'src/libs/ajax/firecloud/FirecloudBucket'; -import { GoogleStorage } from 'src/libs/ajax/GoogleStorage'; -import { Groups } from 'src/libs/ajax/Groups'; import { Apps } from 'src/libs/ajax/leonardo/Apps'; -import { Disks } from 'src/libs/ajax/leonardo/Disks'; import { Runtimes } from 'src/libs/ajax/leonardo/Runtimes'; -import { Methods } from 'src/libs/ajax/methods/Methods'; -import { Metrics } from 'src/libs/ajax/Metrics'; -import { OAuth2 } from 'src/libs/ajax/OAuth2'; -import { SamResources } from 'src/libs/ajax/SamResources'; -import { Support } from 'src/libs/ajax/Support'; -import { Surveys } from 'src/libs/ajax/surveys/Surveys'; -import { TermsOfService } from 'src/libs/ajax/TermsOfService'; -import { User } from 'src/libs/ajax/User'; -import { Cbas } from 'src/libs/ajax/workflows-app/Cbas'; -import { CromIAM } from 'src/libs/ajax/workflows-app/CromIAM'; -import { CromwellApp } from 'src/libs/ajax/workflows-app/CromwellApp'; -import { WorkflowScript } from 'src/libs/ajax/workflows-app/WorkflowScript'; -import { WorkspaceData } from 'src/libs/ajax/WorkspaceDataService'; -import { WorkspaceManagerResources } from 'src/libs/ajax/WorkspaceManagerResources'; import { Workspaces } from 'src/libs/ajax/workspaces/Workspaces'; -export const Ajax = (signal?: AbortSignal) => { +const AjaxTestingRoot = (signal?: AbortSignal) => { return { Apps: Apps(signal), // used for e2e testing - AzureStorage: AzureStorage(signal), - Billing: Billing(signal), - Buckets: GoogleStorage(signal), // used for e2e testing - Catalog: Catalog(signal), - Cbas: Cbas(signal), - CromIAM: CromIAM(signal), - CromwellApp: CromwellApp(signal), - DataRepo: DataRepo(signal), - Disks: Disks(signal), - Dockstore: Dockstore(signal), - DrsUriResolver: DrsUriResolver(signal), - ExternalCredentials: ExternalCredentials(signal), - FirecloudBucket: FirecloudBucket(signal), - Groups: Groups(signal), - Methods: Methods(signal), - Metrics: Metrics(signal), - OAuth2: OAuth2(signal), Runtimes: Runtimes(signal), // used for e2e testing - SamResources: SamResources(signal), - Support: Support(signal), - Surveys: Surveys(signal), - TermsOfService: TermsOfService(signal), - User: User(signal), - WorkflowScript: WorkflowScript(signal), - WorkspaceData: WorkspaceData(signal), - WorkspaceManagerResources: WorkspaceManagerResources(signal), Workspaces: Workspaces(signal), // used for e2e testing }; }; -export type AjaxContract = ReturnType; +export type AjaxTestingContract = ReturnType; -// Exposing Ajax for use by integration tests (and debugging, or whatever) -(window as any).Ajax = Ajax; +export const setupAjaxTestUtil = () => { + // Exposing Ajax for use by integration tests (and debugging, or whatever) + (window as any).Ajax = AjaxTestingRoot; +}; diff --git a/src/libs/ajax/AzureStorage.ts b/src/libs/ajax/AzureStorage.ts index 410a257ed7..285304832d 100644 --- a/src/libs/ajax/AzureStorage.ts +++ b/src/libs/ajax/AzureStorage.ts @@ -3,9 +3,9 @@ import { AnalysisFile, AnalysisFileMetadata } from 'src/analysis/useAnalysisFile import { AbsolutePath, getDisplayName, getExtension, getFileName } from 'src/analysis/utils/file-utils'; import { runtimeToolLabels } from 'src/analysis/utils/tool-utils'; import { authOpts } from 'src/auth/auth-session'; -import { Ajax } from 'src/libs/ajax'; import { fetchWorkspaceManager } from 'src/libs/ajax/ajax-common'; import { fetchOk } from 'src/libs/ajax/fetch/fetch-core'; +import { WorkspaceManagerResources } from 'src/libs/ajax/WorkspaceManagerResources'; import { getConfig } from 'src/libs/config'; import * as Utils from 'src/libs/utils'; import { cloudProviderTypes } from 'src/workspaces/utils'; @@ -78,7 +78,7 @@ export const AzureStorage = (signal?: AbortSignal) => ({ * (which is an expected transient state while a workspace is being cloned). */ containerInfo: async (workspaceId: string): Promise => { - const data = await Ajax(signal).WorkspaceManagerResources.controlledResources(workspaceId); + const data = await WorkspaceManagerResources(signal).controlledResources(workspaceId); const container = _.find( { metadata: { diff --git a/src/libs/ajax/SamResources.ts b/src/libs/ajax/SamResources.ts index 44540f638a..68975b1e8d 100644 --- a/src/libs/ajax/SamResources.ts +++ b/src/libs/ajax/SamResources.ts @@ -1,7 +1,6 @@ import { jsonBody } from '@terra-ui-packages/data-client-core'; import _ from 'lodash/fp'; import { authOpts } from 'src/auth/auth-session'; -import { Ajax } from 'src/libs/ajax'; import { fetchSam } from 'src/libs/ajax/ajax-common'; import { appIdentifier } from 'src/libs/ajax/fetch/fetch-core'; @@ -34,7 +33,7 @@ export const SamResources = (signal?: AbortSignal) => ({ object: string, requesterPaysProject: RequesterPaysProject = undefined ): Promise => { - return Ajax(signal).SamResources.getRequesterPaysSignedUrl(`gs://${bucket}/${object}`, requesterPaysProject); + return SamResources(signal).getRequesterPaysSignedUrl(`gs://${bucket}/${object}`, requesterPaysProject); }, getResourcePolicies: async (fqResourceId: FullyQualifiedResourceId): Promise => { diff --git a/src/libs/ajax/compute-image-providers/ComputeImageProvider.ts b/src/libs/ajax/compute-image-providers/ComputeImageProvider.ts index a943be8f6b..4ce6f43ce6 100644 --- a/src/libs/ajax/compute-image-providers/ComputeImageProvider.ts +++ b/src/libs/ajax/compute-image-providers/ComputeImageProvider.ts @@ -5,7 +5,7 @@ import { runtimeToolLabels, terraSupportedRuntimeImageIds, } from 'src/analysis/utils/tool-utils'; -import { Ajax } from 'src/libs/ajax'; +import { GoogleStorage } from 'src/libs/ajax/GoogleStorage'; import { getConfig } from 'src/libs/config'; export interface ComputeImageProviderContract { @@ -72,8 +72,8 @@ const normalizeImage: (rawImage: ComputeImageRaw) => ComputeImage = (rawImage) = export const ComputeImageProvider: ComputeImageProviderContract = { listImages: async (googleProject: string, signal?: AbortSignal): Promise => { - const fetchedImages: ComputeImageRaw[] = await Ajax(signal) - .Buckets.getObjectPreview( + const fetchedImages: ComputeImageRaw[] = await GoogleStorage(signal) + .getObjectPreview( googleProject, getConfig().terraDockerImageBucket, getConfig().terraDockerVersionsFile, diff --git a/src/libs/ajax/data-table-providers/WdsDataTableProvider.test.ts b/src/libs/ajax/data-table-providers/WdsDataTableProvider.test.ts index 6275902ff4..f0b079134f 100644 --- a/src/libs/ajax/data-table-providers/WdsDataTableProvider.test.ts +++ b/src/libs/ajax/data-table-providers/WdsDataTableProvider.test.ts @@ -1,8 +1,7 @@ import { DeepPartial } from '@terra-ui-packages/core-utils'; -import { Ajax } from 'src/libs/ajax'; -import { Apps } from 'src/libs/ajax/leonardo/Apps'; -import { Capabilities, WorkspaceData } from 'src/libs/ajax/WorkspaceDataService'; -import { asMockedFn } from 'src/testing/test-utils'; +import { Apps, AppsAjaxContract } from 'src/libs/ajax/leonardo/Apps'; +import { Capabilities, WorkspaceData, WorkspaceDataAjaxContract } from 'src/libs/ajax/WorkspaceDataService'; +import { asMockedFn, MockedFn, partial } from 'src/testing/test-utils'; import { cloudProviderTypes } from 'src/workspaces/utils'; import { ListAppItem } from '../leonardo/models/app-models'; @@ -22,7 +21,8 @@ import { wdsToEntityServiceMetadata, } from './WdsDataTableProvider'; -jest.mock('src/libs/ajax'); +jest.mock('src/libs/ajax/leonardo/Apps'); +jest.mock('src/libs/ajax/WorkspaceDataService'); type ReactNotificationsComponentExports = typeof import('react-notifications-component'); jest.mock('react-notifications-component', (): DeepPartial => { @@ -97,12 +97,8 @@ const queryOptions: EntityQueryOptions = { columnFilter: '', }; -type WorkspaceDataContract = ReturnType; -type AjaxContract = ReturnType; -type AppsContract = ReturnType; - describe('WdsDataTableProvider', () => { - const getRecordsMockImpl: WorkspaceDataContract['getRecords'] = ( + const getRecordsMockImpl: WorkspaceDataAjaxContract['getRecords'] = ( _root: string, _instanceId: string, _recordType: string, @@ -150,20 +146,20 @@ describe('WdsDataTableProvider', () => { return Promise.resolve(recordQueryResponse); }; - const getCapabilitiesMockImpl: WorkspaceDataContract['getCapabilities'] = (_root: string) => { + const getCapabilitiesMockImpl: WorkspaceDataAjaxContract['getCapabilities'] = (_root: string) => { const Capabilities: Capabilities = {}; return Promise.resolve(Capabilities); }; - const deleteTableMockImpl: WorkspaceDataContract['deleteTable'] = (_instanceId: string, _recordType: string) => { + const deleteTableMockImpl: WorkspaceDataAjaxContract['deleteTable'] = (_instanceId: string, _recordType: string) => { return Promise.resolve(new Response('', { status: 204 })); }; - const downloadTsvMockImpl: WorkspaceDataContract['downloadTsv'] = (_instanceId: string, _recordType: string) => { + const downloadTsvMockImpl: WorkspaceDataAjaxContract['downloadTsv'] = (_instanceId: string, _recordType: string) => { return Promise.resolve(new Blob(['hello'])); }; - const uploadTsvMockImpl: WorkspaceDataContract['uploadTsv'] = ( + const uploadTsvMockImpl: WorkspaceDataAjaxContract['uploadTsv'] = ( _root: string, _instanceId: string, _recordType: string, @@ -172,7 +168,7 @@ describe('WdsDataTableProvider', () => { return Promise.resolve({ message: 'Upload Succeeded', recordsModified: 1 }); }; - const updateRecordMockImpl: WorkspaceDataContract['updateRecord'] = ( + const updateRecordMockImpl: WorkspaceDataAjaxContract['updateRecord'] = ( _root: string, _instanceId: string, _recordType: string, @@ -190,37 +186,34 @@ describe('WdsDataTableProvider', () => { return Promise.resolve(testProxyUrlResponse); }; - let getRecords: jest.MockedFunction; - let updateRecord: jest.MockedFunction; - let getCapabilities: jest.MockedFunction; - let deleteTable: jest.MockedFunction; - let downloadTsv: jest.MockedFunction; - let uploadTsv: jest.MockedFunction; - let listAppsV2: jest.MockedFunction; + const getRecords: MockedFn = jest.fn(); + const updateRecord: MockedFn = jest.fn(); + const getCapabilities: MockedFn = jest.fn(); + const deleteTable: MockedFn = jest.fn(); + const downloadTsv: MockedFn = jest.fn(); + const uploadTsv: MockedFn = jest.fn(); + const listAppsV2: MockedFn = jest.fn(); beforeEach(() => { - getRecords = jest.fn().mockImplementation(getRecordsMockImpl); - updateRecord = jest.fn().mockImplementation(updateRecordMockImpl); - getCapabilities = jest.fn().mockImplementation(getCapabilitiesMockImpl); - deleteTable = jest.fn().mockImplementation(deleteTableMockImpl); - downloadTsv = jest.fn().mockImplementation(downloadTsvMockImpl); - uploadTsv = jest.fn().mockImplementation(uploadTsvMockImpl); - listAppsV2 = jest.fn().mockImplementation(listAppsV2MockImpl); - - asMockedFn(Ajax).mockImplementation( - () => - ({ - WorkspaceData: { - getRecords, - updateRecord, - getCapabilities, - deleteTable, - downloadTsv, - uploadTsv, - } as Partial, - Apps: { listAppsV2 } as Partial, - } as Partial as AjaxContract) + getRecords.mockImplementation(getRecordsMockImpl); + updateRecord.mockImplementation(updateRecordMockImpl); + getCapabilities.mockImplementation(getCapabilitiesMockImpl); + deleteTable.mockImplementation(deleteTableMockImpl); + downloadTsv.mockImplementation(downloadTsvMockImpl); + uploadTsv.mockImplementation(uploadTsvMockImpl); + listAppsV2.mockImplementation(listAppsV2MockImpl); + + asMockedFn(WorkspaceData).mockReturnValue( + partial({ + getRecords, + updateRecord, + getCapabilities, + deleteTable, + downloadTsv, + uploadTsv, + }) ); + asMockedFn(Apps).mockReturnValue(partial({ listAppsV2 })); }); describe('transformAttributes', () => { diff --git a/src/libs/ajax/data-table-providers/WdsDataTableProvider.ts b/src/libs/ajax/data-table-providers/WdsDataTableProvider.ts index bd8827efaa..a1981fecac 100644 --- a/src/libs/ajax/data-table-providers/WdsDataTableProvider.ts +++ b/src/libs/ajax/data-table-providers/WdsDataTableProvider.ts @@ -1,5 +1,4 @@ import _ from 'lodash/fp'; -import { Ajax } from 'src/libs/ajax'; import { AttributeArray, DataTableFeatures, @@ -15,6 +14,7 @@ import { UploadParameters, } from 'src/libs/ajax/data-table-providers/DataTableProvider'; import { LeoAppStatus, ListAppItem } from 'src/libs/ajax/leonardo/models/app-models'; +import { WorkspaceData } from 'src/libs/ajax/WorkspaceDataService'; import { Capabilities, Capability } from 'src/libs/ajax/WorkspaceDataService'; import { notificationStore } from 'src/libs/state'; import * as Utils from 'src/libs/utils'; @@ -283,7 +283,7 @@ export class WdsDataTableProvider implements DataTableProvider { metadata: EntityMetadata ): Promise => { if (!this.proxyUrl) return Promise.reject('Proxy Url not loaded'); - const wdsPage: RecordQueryResponse = await Ajax(signal).WorkspaceData.getRecords( + const wdsPage: RecordQueryResponse = await WorkspaceData(signal).getRecords( this.proxyUrl, this.workspaceId, entityType, @@ -301,17 +301,17 @@ export class WdsDataTableProvider implements DataTableProvider { deleteTable = (entityType: string): Promise => { if (!this.proxyUrl) return Promise.reject('Proxy Url not loaded'); - return Ajax().WorkspaceData.deleteTable(this.proxyUrl, this.workspaceId, entityType); + return WorkspaceData().deleteTable(this.proxyUrl, this.workspaceId, entityType); }; deleteColumn = (signal: AbortSignal, entityType: string, attributeName: string): Promise => { if (!this.proxyUrl) return Promise.reject('Proxy URL not loaded'); - return Ajax(signal).WorkspaceData.deleteColumn(this.proxyUrl, this.workspaceId, entityType, attributeName); + return WorkspaceData(signal).deleteColumn(this.proxyUrl, this.workspaceId, entityType, attributeName); }; downloadTsv = (signal: AbortSignal, entityType: string): Promise => { if (!this.proxyUrl) return Promise.reject('Proxy Url not loaded'); - return Ajax(signal).WorkspaceData.downloadTsv(this.proxyUrl, this.workspaceId, entityType); + return WorkspaceData(signal).downloadTsv(this.proxyUrl, this.workspaceId, entityType); }; uploadTsv = (uploadParams: UploadParameters): Promise => { @@ -331,7 +331,7 @@ export class WdsDataTableProvider implements DataTableProvider { ); } }, 1000); - return Ajax().WorkspaceData.uploadTsv( + return WorkspaceData().uploadTsv( this.proxyUrl, uploadParams.workspaceId, uploadParams.recordType, @@ -342,7 +342,7 @@ export class WdsDataTableProvider implements DataTableProvider { updateRecord = (recordEditParams: RecordEditParameters): Promise => { if (!this.proxyUrl) return Promise.reject('Proxy Url not loaded'); - return Ajax().WorkspaceData.updateRecord( + return WorkspaceData().updateRecord( this.proxyUrl, recordEditParams.instance, recordEditParams.recordName, @@ -353,7 +353,7 @@ export class WdsDataTableProvider implements DataTableProvider { updateAttribute = (params: UpdateAttributeParameters): Promise => { if (!this.proxyUrl) return Promise.reject('Proxy Url not loaded'); - return Ajax().WorkspaceData.updateAttribute( + return WorkspaceData().updateAttribute( this.proxyUrl, this.workspaceId, params.entityType, diff --git a/src/libs/ajax/file-browser-providers/GCSFileBrowserProvider.test.ts b/src/libs/ajax/file-browser-providers/GCSFileBrowserProvider.test.ts index ae95d93fa7..dd35fa5813 100644 --- a/src/libs/ajax/file-browser-providers/GCSFileBrowserProvider.test.ts +++ b/src/libs/ajax/file-browser-providers/GCSFileBrowserProvider.test.ts @@ -1,11 +1,13 @@ -import { Ajax } from 'src/libs/ajax'; import { FileBrowserDirectory, FileBrowserFile } from 'src/libs/ajax/file-browser-providers/FileBrowserProvider'; import GCSFileBrowserProvider from 'src/libs/ajax/file-browser-providers/GCSFileBrowserProvider'; +import { GoogleStorage } from 'src/libs/ajax/GoogleStorage'; import { GCSItem, GCSListObjectsResponse, GoogleStorageContract } from 'src/libs/ajax/GoogleStorage'; +import { SamResources, SamResourcesContract } from 'src/libs/ajax/SamResources'; import * as Utils from 'src/libs/utils'; -import { asMockedFn } from 'src/testing/test-utils'; +import { asMockedFn, MockedFn, partial } from 'src/testing/test-utils'; -jest.mock('src/libs/ajax'); +jest.mock('src/libs/ajax/GoogleStorage'); +jest.mock('src/libs/ajax/SamResources'); const gcsObject = (name: string): GCSItem => ({ bucket: 'test-bucket', @@ -37,10 +39,11 @@ const expectedFile = (path: string): FileBrowserFile => ({ }); describe('GCSFileBrowserProvider', () => { - let list; + let list: MockedFn; beforeEach(() => { - list = jest.fn().mockImplementation((_googleProject, _bucket, _prefix, options = {}) => { + list = jest.fn(); + list.mockImplementation((_googleProject, _bucket, _prefix, options = {}) => { const { pageToken } = options; const response: GCSListObjectsResponse = Utils.switchCase( @@ -73,7 +76,7 @@ describe('GCSFileBrowserProvider', () => { return Promise.resolve(response); }); - asMockedFn(Ajax).mockImplementation(() => ({ Buckets: { list } } as ReturnType)); + asMockedFn(GoogleStorage).mockReturnValue(partial({ list })); }); it('pages through files (objects)', async () => { @@ -140,12 +143,10 @@ describe('GCSFileBrowserProvider', () => { it('gets a signed URL for downloads', async () => { // Arrange - const getSignedUrl = jest.fn(() => Promise.resolve('signedUrl')); - asMockedFn(Ajax).mockImplementation(() => { - return { - SamResources: { getSignedUrl } as Partial['SamResources']>, - } as ReturnType; - }); + const getSignedUrl: MockedFn = jest.fn(); + getSignedUrl.mockResolvedValue('signedUrl'); + + asMockedFn(SamResources).mockReturnValue(partial({ getSignedUrl })); const provider = GCSFileBrowserProvider({ bucket: 'test-bucket', project: 'test-project' }); @@ -170,13 +171,10 @@ describe('GCSFileBrowserProvider', () => { it('uploads a file', async () => { // Arrange - const upload = jest.fn(() => Promise.resolve()); - asMockedFn(Ajax).mockImplementation( - () => - ({ - Buckets: { upload } as Partial, - } as ReturnType) - ); + const upload: MockedFn = jest.fn(); + upload.mockResolvedValue(undefined); + + asMockedFn(GoogleStorage).mockReturnValue(partial({ upload })); const testFile = new File(['somecontent'], 'example.txt', { type: 'text/text' }); @@ -191,13 +189,9 @@ describe('GCSFileBrowserProvider', () => { it('deletes files', async () => { // Arrange - const del = jest.fn(() => Promise.resolve()); - asMockedFn(Ajax).mockImplementation( - () => - ({ - Buckets: { delete: del } as Partial, - } as ReturnType) - ); + const del: MockedFn = jest.fn(); + del.mockResolvedValue(undefined); + asMockedFn(GoogleStorage).mockReturnValue(partial({ delete: del })); const provider = GCSFileBrowserProvider({ bucket: 'test-bucket', project: 'test-project' }); @@ -210,16 +204,16 @@ describe('GCSFileBrowserProvider', () => { it('moves files', async () => { // Arrange - const copyWithinBucket = jest.fn(() => Promise.resolve()); - const del = jest.fn(() => Promise.resolve()); - asMockedFn(Ajax).mockImplementation( - () => - ({ - Buckets: { - copyWithinBucket, - delete: del, - } as Partial, - } as ReturnType) + const copyWithinBucket: MockedFn = jest.fn(); + copyWithinBucket.mockResolvedValue(undefined); + const del: MockedFn = jest.fn(); + del.mockResolvedValue(undefined); + + asMockedFn(GoogleStorage).mockReturnValue( + partial({ + copyWithinBucket, + delete: del, + }) ); const provider = GCSFileBrowserProvider({ bucket: 'test-bucket', project: 'test-project' }); @@ -239,13 +233,10 @@ describe('GCSFileBrowserProvider', () => { it('creates empty directories', async () => { // Arrange - const upload = jest.fn(() => Promise.resolve()); - asMockedFn(Ajax).mockImplementation( - () => - ({ - Buckets: { upload } as Partial, - } as ReturnType) - ); + const upload: MockedFn = jest.fn(); + upload.mockResolvedValue(undefined); + + asMockedFn(GoogleStorage).mockReturnValue(partial({ upload })); const provider = GCSFileBrowserProvider({ bucket: 'test-bucket', project: 'test-project' }); @@ -265,13 +256,10 @@ describe('GCSFileBrowserProvider', () => { it('deletes empty directories', async () => { // Arrange - const del = jest.fn(() => Promise.resolve()); - asMockedFn(Ajax).mockImplementation( - () => - ({ - Buckets: { delete: del } as Partial, - } as ReturnType) - ); + const del: MockedFn = jest.fn(); + del.mockResolvedValue(undefined); + + asMockedFn(GoogleStorage).mockReturnValue(partial({ delete: del })); const provider = GCSFileBrowserProvider({ bucket: 'test-bucket', project: 'test-project' }); diff --git a/src/libs/ajax/file-browser-providers/GCSFileBrowserProvider.ts b/src/libs/ajax/file-browser-providers/GCSFileBrowserProvider.ts index 833ed51957..422f659859 100644 --- a/src/libs/ajax/file-browser-providers/GCSFileBrowserProvider.ts +++ b/src/libs/ajax/file-browser-providers/GCSFileBrowserProvider.ts @@ -1,7 +1,8 @@ -import { Ajax } from 'src/libs/ajax'; import FileBrowserProvider from 'src/libs/ajax/file-browser-providers/FileBrowserProvider'; +import { GoogleStorage } from 'src/libs/ajax/GoogleStorage'; import { GCSItem } from 'src/libs/ajax/GoogleStorage'; import IncrementalResponse from 'src/libs/ajax/incremental-response/IncrementalResponse'; +import { SamResources } from 'src/libs/ajax/SamResources'; export interface GCSFileBrowserProviderParams { bucket: string; @@ -67,7 +68,7 @@ const GCSFileBrowserProvider = ({ requestOptions.matchGlob = matchGlob; } - const response = await Ajax(signal).Buckets.list(project, bucket, prefix, requestOptions); + const response = await GoogleStorage(signal).list(project, bucket, prefix, requestOptions); const responseItems = (response[itemsOrPrefixes] || []).map((itemOrPrefix) => mapItemOrPrefix(itemOrPrefix)); // Exclude folder placeholder objects. @@ -141,20 +142,20 @@ const GCSFileBrowserProvider = ({ signal, }), getDownloadUrlForFile: async (path, { signal } = {}) => { - return await Ajax(signal).SamResources.getSignedUrl(bucket, path); + return await SamResources(signal).getSignedUrl(bucket, path); }, getDownloadCommandForFile: (path) => { return Promise.resolve(`gcloud storage cp 'gs://${bucket}/${path}' .`); }, uploadFileToDirectory: (directoryPath, file) => { - return Ajax().Buckets.upload(project, bucket, directoryPath, file); + return GoogleStorage().upload(project, bucket, directoryPath, file); }, deleteFile: async (path: string): Promise => { - await Ajax().Buckets.delete(project, bucket, path); + await GoogleStorage().delete(project, bucket, path); }, moveFile: async (sourcePath: string, destinationPath: string): Promise => { - await Ajax().Buckets.copyWithinBucket(project, bucket, sourcePath, destinationPath); - await Ajax().Buckets.delete(project, bucket, sourcePath); + await GoogleStorage().copyWithinBucket(project, bucket, sourcePath, destinationPath); + await GoogleStorage().delete(project, bucket, sourcePath); }, createEmptyDirectory: async (directoryPath: string) => { // Create a placeholder object for the new folder. @@ -164,7 +165,7 @@ const GCSFileBrowserProvider = ({ const prefix = prefixSegments.length === 0 ? '' : `${prefixSegments.join('/')}/`; const directoryName = directoryPath.split('/').slice(-2, -1)[0]; const placeholderObject = new File([''], `${directoryName}/`, { type: 'text/plain' }); - await Ajax().Buckets.upload(project, bucket, prefix, placeholderObject); + await GoogleStorage().upload(project, bucket, prefix, placeholderObject); return { path: directoryPath, }; @@ -175,7 +176,7 @@ const GCSFileBrowserProvider = ({ // A placeholder object may not exist for the prefix being viewed, so do not an report error for 404 responses. // See https://cloud.google.com/storage/docs/folders for more information on placeholder objects. try { - await Ajax().Buckets.delete(project, bucket, directoryPath); + await GoogleStorage().delete(project, bucket, directoryPath); } catch (error) { if (!(error instanceof Response && error.status === 404)) { throw error; diff --git a/src/libs/ajax/workspaces/Workspaces.ts b/src/libs/ajax/workspaces/Workspaces.ts index dcc8c6947c..aa8e1fee75 100644 --- a/src/libs/ajax/workspaces/Workspaces.ts +++ b/src/libs/ajax/workspaces/Workspaces.ts @@ -337,7 +337,7 @@ export const Workspaces = (signal?: AbortSignal) => ({ }, // NB: This could one day perhaps redirect to CromIAM's 'workflow' like: - // workflow: workflowId => Ajax(signal).CromIAM.workflow(workflowId) + // workflow: workflowId => CromIAM(signal).workflow(workflowId) // But: Because of the slowness of asking via CromIAM, that's probably a non-starter for right now. workflow: (workflowId: string) => { return { diff --git a/src/support/ResourcePolicies.test.tsx b/src/support/ResourcePolicies.test.tsx index 8ef5e03781..f12d8d1c83 100644 --- a/src/support/ResourcePolicies.test.tsx +++ b/src/support/ResourcePolicies.test.tsx @@ -24,7 +24,7 @@ describe('ResourcePolicies', () => { asMockedFn(SamResources).mockReturnValue(partial({ getResourcePolicies })); } - it('calls Ajax().SamResources.getResourcePolicies and displays the result', async () => { + it('calls SamResources().getResourcePolicies and displays the result', async () => { // Arrange const testValue = uuidv4(); const getResourcePolicies = jest.fn(() => Promise.resolve({ policy: testValue })); diff --git a/src/workflows-app/RunDetails.test.ts b/src/workflows-app/RunDetails.test.ts index 084c8bc690..c71309b813 100644 --- a/src/workflows-app/RunDetails.test.ts +++ b/src/workflows-app/RunDetails.test.ts @@ -2,7 +2,6 @@ import { act, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import _ from 'lodash/fp'; import { h } from 'react-hyperscript-helpers'; -import { AjaxContract } from 'src/libs/ajax'; import { AzureBlobByUriContract, AzureBlobResult, @@ -95,7 +94,7 @@ const runDetailsProps = { const captureEvent = jest.fn(); -const mockObj: Pick = { +const mockObj = { CromwellApp: partial({ workflows: () => partial({