From 6606b54dbbef097467b002e62350071c34ec2e04 Mon Sep 17 00:00:00 2001 From: Miguel Silva Date: Fri, 24 Jan 2025 13:03:44 -0500 Subject: [PATCH 1/2] UIE-204 Narrow Ajax Usage pt22 - just getting started... --- .../AnalysisProvider.test.ts | 63 +++++++------------ .../analysis-providers/AnalysisProvider.ts | 27 ++++---- 2 files changed, 37 insertions(+), 53 deletions(-) diff --git a/src/libs/ajax/analysis-providers/AnalysisProvider.test.ts b/src/libs/ajax/analysis-providers/AnalysisProvider.test.ts index b20e4a7db4..c90adfbb60 100644 --- a/src/libs/ajax/analysis-providers/AnalysisProvider.test.ts +++ b/src/libs/ajax/analysis-providers/AnalysisProvider.test.ts @@ -1,75 +1,58 @@ import { AbsolutePath } from 'src/analysis/utils/file-utils'; import { runtimeToolLabels } from 'src/analysis/utils/tool-utils'; -import { Ajax } from 'src/libs/ajax'; import { AnalysisProvider } from 'src/libs/ajax/analysis-providers/AnalysisProvider'; -import { asMockedFn } from 'src/testing/test-utils'; +import { AzureStorage, AzureStorageContract } from 'src/libs/ajax/AzureStorage'; +import { GoogleStorage, GoogleStorageContract } from 'src/libs/ajax/GoogleStorage'; +import { asMockedFn, MockedFn, partial } from 'src/testing/test-utils'; import { WorkspaceInfo } from 'src/workspaces/utils'; -type AjaxExports = typeof import('src/libs/ajax'); -jest.mock('src/libs/ajax', (): AjaxExports => { - return { - ...jest.requireActual('src/libs/ajax'), - Ajax: jest.fn(), - }; -}); +jest.mock('src/libs/ajax/AzureStorage'); +jest.mock('src/libs/ajax/GoogleStorage'); -type AjaxContract = ReturnType; -type AjaxBucketsContract = AjaxContract['Buckets']; -type AjaxBucketsAnalysisContract = ReturnType; -type AjaxAzureStorageContract = AjaxContract['AzureStorage']; -type AjaxAzureStorageBlobContract = ReturnType; +type AjaxBucketsAnalysisContract = ReturnType; +type AjaxAzureStorageBlobContract = ReturnType; describe('AnalysisProvider - listAnalyses', () => { it('handles GCP workspace', async () => { // Arrange - const mockBuckets: Partial = { - listAnalyses: jest.fn(), - }; - asMockedFn((mockBuckets as AjaxBucketsContract).listAnalyses).mockResolvedValue([]); + const listAnalyses: MockedFn = jest.fn(); + listAnalyses.mockResolvedValue([]); - const mockAjax: Partial = { - Buckets: mockBuckets as AjaxBucketsContract, - }; - asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract); + asMockedFn(GoogleStorage).mockReturnValue(partial({ listAnalyses })); - const workspaceInfo: Partial = { + const workspaceInfo = partial({ googleProject: 'GoogleProject123', bucketName: 'Bucket123', cloudPlatform: 'Gcp', - }; + }); // Act - const results = await AnalysisProvider.listAnalyses(workspaceInfo as WorkspaceInfo); + const results = await AnalysisProvider.listAnalyses(workspaceInfo); // Assert expect(results).toEqual([]); - expect(mockBuckets.listAnalyses).toBeCalledTimes(1); - expect(mockBuckets.listAnalyses).toBeCalledWith('GoogleProject123', 'Bucket123'); + expect(listAnalyses).toBeCalledTimes(1); + expect(listAnalyses).toBeCalledWith('GoogleProject123', 'Bucket123'); }); it('handles Azure workspace', async () => { // Arrange - const mockAzureStorage: Partial = { - listNotebooks: jest.fn(), - }; - asMockedFn((mockAzureStorage as AjaxAzureStorageContract).listNotebooks).mockResolvedValue([]); + const listNotebooks: MockedFn = jest.fn(); + listNotebooks.mockResolvedValue([]); - const mockAjax: Partial = { - AzureStorage: mockAzureStorage as AjaxAzureStorageContract, - }; - asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract); + asMockedFn(AzureStorage).mockReturnValue(partial({ listNotebooks })); - const workspaceInfo: Partial = { + const workspaceInfo = partial({ workspaceId: 'Workspace123', - }; + }); // Act - const results = await AnalysisProvider.listAnalyses(workspaceInfo as WorkspaceInfo); + const results = await AnalysisProvider.listAnalyses(workspaceInfo); // Assert expect(results).toEqual([]); - expect(mockAzureStorage.listNotebooks).toBeCalledTimes(1); - expect(mockAzureStorage.listNotebooks).toBeCalledWith('Workspace123'); + expect(listNotebooks).toBeCalledTimes(1); + expect(listNotebooks).toBeCalledWith('Workspace123'); }); }); diff --git a/src/libs/ajax/analysis-providers/AnalysisProvider.ts b/src/libs/ajax/analysis-providers/AnalysisProvider.ts index 718513e93a..080c81b224 100644 --- a/src/libs/ajax/analysis-providers/AnalysisProvider.ts +++ b/src/libs/ajax/analysis-providers/AnalysisProvider.ts @@ -1,7 +1,8 @@ import { AnalysisFile } from 'src/analysis/useAnalysisFiles'; import { AbsolutePath, getExtension, getFileName, stripExtension } from 'src/analysis/utils/file-utils'; import { getToolLabelFromFileExtension, ToolLabel } from 'src/analysis/utils/tool-utils'; -import { Ajax } from 'src/libs/ajax'; +import { AzureStorage } from 'src/libs/ajax/AzureStorage'; +import { GoogleStorage } from 'src/libs/ajax/GoogleStorage'; import { GoogleWorkspaceInfo, isGoogleWorkspaceInfo, WorkspaceInfo } from 'src/workspaces/utils'; export interface AnalysisProviderContract { @@ -27,9 +28,9 @@ export interface AnalysisProviderContract { export const AnalysisProvider: AnalysisProviderContract = { listAnalyses: async (workspaceInfo: WorkspaceInfo, signal?: AbortSignal): Promise => { const selectedAnalyses: AnalysisFile[] = isGoogleWorkspaceInfo(workspaceInfo) - ? await Ajax(signal).Buckets.listAnalyses(workspaceInfo.googleProject, workspaceInfo.bucketName) + ? await GoogleStorage(signal).listAnalyses(workspaceInfo.googleProject, workspaceInfo.bucketName) : // TODO: cleanup once TS is merged in for AzureStorage module - ((await Ajax(signal).AzureStorage.listNotebooks(workspaceInfo.workspaceId)) as any); + ((await AzureStorage(signal).listNotebooks(workspaceInfo.workspaceId)) as any); return selectedAnalyses; }, copyAnalysis: async ( @@ -41,13 +42,13 @@ export const AnalysisProvider: AnalysisProviderContract = { signal?: AbortSignal ): Promise => { if (isGoogleWorkspaceInfo(sourceWorkspace)) { - await Ajax(signal) - .Buckets.analysis(sourceWorkspace.googleProject, sourceWorkspace.bucketName, printName, toolLabel) + await GoogleStorage(signal) + .analysis(sourceWorkspace.googleProject, sourceWorkspace.bucketName, printName, toolLabel) // assumes GCP to GCP copy .copy(`${newName}.${getExtension(printName)}`, (targetWorkspace as GoogleWorkspaceInfo).bucketName, false); } else { - await Ajax(signal) - .AzureStorage.blob(sourceWorkspace.workspaceId, printName) + await AzureStorage(signal) + .blob(sourceWorkspace.workspaceId, printName) .copy(stripExtension(newName), targetWorkspace.workspaceId); } }, @@ -59,21 +60,21 @@ export const AnalysisProvider: AnalysisProviderContract = { signal?: AbortSignal ): Promise => { isGoogleWorkspaceInfo(workspaceInfo) - ? await Ajax(signal) - .Buckets.analysis(workspaceInfo.googleProject, workspaceInfo.bucketName, fullAnalysisName, toolLabel) + ? await GoogleStorage(signal) + .analysis(workspaceInfo.googleProject, workspaceInfo.bucketName, fullAnalysisName, toolLabel) .create(contents) - : await Ajax(signal).AzureStorage.blob(workspaceInfo.workspaceId, fullAnalysisName).create(contents); + : await AzureStorage(signal).blob(workspaceInfo.workspaceId, fullAnalysisName).create(contents); }, deleteAnalysis: async (workspaceInfo: WorkspaceInfo, path: AbsolutePath, signal?: AbortSignal): Promise => { isGoogleWorkspaceInfo(workspaceInfo) - ? await Ajax(signal) - .Buckets.analysis( + ? await GoogleStorage(signal) + .analysis( workspaceInfo.googleProject, workspaceInfo.bucketName, getFileName(path), getToolLabelFromFileExtension(getExtension(path)) ) .delete() - : await Ajax(signal).AzureStorage.blob(workspaceInfo.workspaceId, getFileName(path)).delete(); + : await AzureStorage(signal).blob(workspaceInfo.workspaceId, getFileName(path)).delete(); }, }; From 1c71bbe3b4fb335d16ee213fe6f048c0c605eb60 Mon Sep 17 00:00:00 2001 From: Miguel Silva Date: Tue, 28 Jan 2025 09:00:34 -0500 Subject: [PATCH 2/2] UIE-204 Narrow Ajax Usage pt23 - narrow Ajax() usage within some of src/libs/ajax area to call Ajax().SubAreaX directly. - improve mock types where possible --- .../AnalysisProvider.test.ts | 165 +++++------------- .../leonardo/providers/LeoAppProvider.test.ts | 50 +++--- .../ajax/leonardo/providers/LeoAppProvider.ts | 14 +- .../providers/LeoDiskProvider.test.ts | 5 +- .../providers/LeoRuntimeProvider.test.ts | 121 ++++++------- .../leonardo/providers/LeoRuntimeProvider.ts | 34 ++-- .../ExportWorkflowToWorkspaceProvider.test.ts | 94 +++++----- .../ExportWorkflowToWorkspaceProvider.ts | 13 +- .../providers/WorkspaceProvider.test.ts | 25 +-- .../workspaces/providers/WorkspaceProvider.ts | 4 +- .../state/useCloudEnvironmentPolling.test.ts | 5 +- 11 files changed, 211 insertions(+), 319 deletions(-) diff --git a/src/libs/ajax/analysis-providers/AnalysisProvider.test.ts b/src/libs/ajax/analysis-providers/AnalysisProvider.test.ts index c90adfbb60..3d6acd72f3 100644 --- a/src/libs/ajax/analysis-providers/AnalysisProvider.test.ts +++ b/src/libs/ajax/analysis-providers/AnalysisProvider.test.ts @@ -59,23 +59,12 @@ describe('AnalysisProvider - listAnalyses', () => { describe('AnalysisProvider - copyAnalysis', () => { it('handles GCP workspace', async () => { // Arrange - const mockBuckets: Partial = { - analysis: jest.fn(), - }; - const watchCopy = jest.fn(); - asMockedFn((mockBuckets as AjaxBucketsContract).analysis).mockImplementation(() => { - const mockAnalysisContract: Partial = { - copy: watchCopy, - }; - const analysisContract = mockAnalysisContract as AjaxBucketsAnalysisContract; - asMockedFn(analysisContract.copy).mockResolvedValue(undefined); - return analysisContract; - }); + const analysis: MockedFn = jest.fn(); + const watchCopy: MockedFn = jest.fn(); + watchCopy.mockResolvedValue(undefined); - const mockAjax: Partial = { - Buckets: mockBuckets as AjaxBucketsContract, - }; - asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract); + analysis.mockReturnValue(partial({ copy: watchCopy })); + asMockedFn(GoogleStorage).mockReturnValue(partial({ analysis })); const workspaceInfo: Partial = { googleProject: 'GoogleProject123', @@ -97,36 +86,20 @@ describe('AnalysisProvider - copyAnalysis', () => { // Assert expect(result).toEqual(undefined); - expect(mockBuckets.analysis).toBeCalledTimes(1); - expect(mockBuckets.analysis).toBeCalledWith( - 'GoogleProject123', - 'Bucket123', - 'PrintName123.jpt', - runtimeToolLabels.Jupyter - ); + expect(analysis).toBeCalledTimes(1); + expect(analysis).toBeCalledWith('GoogleProject123', 'Bucket123', 'PrintName123.jpt', runtimeToolLabels.Jupyter); expect(watchCopy).toBeCalledTimes(1); expect(watchCopy).toBeCalledWith('NewName123.jpt', 'TargetBucket456', false); }); it('handles Azure workspace', async () => { // Arrange - const mockAzureStorage: Partial = { - blob: jest.fn(), - }; - const watchCopy = jest.fn(); - asMockedFn((mockAzureStorage as AjaxAzureStorageContract).blob).mockImplementation(() => { - const mockBlobContract: Partial = { - copy: watchCopy, - }; - const blobContract = mockBlobContract as AjaxAzureStorageBlobContract; - asMockedFn(blobContract.copy).mockResolvedValue(undefined); - return blobContract; - }); + const blob: MockedFn = jest.fn(); + const watchCopy: MockedFn = jest.fn(); + watchCopy.mockResolvedValue(undefined); - const mockAjax: Partial = { - AzureStorage: mockAzureStorage as AjaxAzureStorageContract, - }; - asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract); + blob.mockReturnValue(partial({ copy: watchCopy })); + asMockedFn(AzureStorage).mockReturnValue(partial({ blob })); const workspaceInfo: Partial = { workspaceId: 'Workspace123', @@ -147,8 +120,8 @@ describe('AnalysisProvider - copyAnalysis', () => { // Assert expect(result).toEqual(undefined); - expect(mockAzureStorage.blob).toBeCalledTimes(1); - expect(mockAzureStorage.blob).toBeCalledWith('Workspace123', 'PrintName123.jpt'); + expect(blob).toBeCalledTimes(1); + expect(blob).toBeCalledWith('Workspace123', 'PrintName123.jpt'); expect(watchCopy).toBeCalledTimes(1); expect(watchCopy).toBeCalledWith('NewName123', 'Workspace456'); }); @@ -157,23 +130,12 @@ describe('AnalysisProvider - copyAnalysis', () => { describe('AnalysisProvider - createAnalysis', () => { it('handles GCP workspace', async () => { // Arrange - const mockBuckets: Partial = { - analysis: jest.fn(), - }; - const watchCreate = jest.fn(); - asMockedFn((mockBuckets as AjaxBucketsContract).analysis).mockImplementation(() => { - const mockAnalysisContract: Partial = { - create: watchCreate, - }; - const analysisContract = mockAnalysisContract as AjaxBucketsAnalysisContract; - asMockedFn(analysisContract.create).mockResolvedValue(undefined); - return analysisContract; - }); + const analysis: MockedFn = jest.fn(); + const watchCreate: MockedFn = jest.fn(); + watchCreate.mockResolvedValue(undefined); - const mockAjax: Partial = { - Buckets: mockBuckets as AjaxBucketsContract, - }; - asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract); + analysis.mockReturnValue(partial({ create: watchCreate })); + asMockedFn(GoogleStorage).mockReturnValue(partial({ analysis })); const workspaceInfo: Partial = { googleProject: 'GoogleProject123', @@ -194,36 +156,20 @@ describe('AnalysisProvider - createAnalysis', () => { // contents: any, signal?: AbortSignal) => Promise // Assert expect(result).toEqual(undefined); - expect(mockBuckets.analysis).toBeCalledTimes(1); - expect(mockBuckets.analysis).toBeCalledWith( - 'GoogleProject123', - 'Bucket123', - 'PrintName123.ipynb', - runtimeToolLabels.Jupyter - ); + expect(analysis).toBeCalledTimes(1); + expect(analysis).toBeCalledWith('GoogleProject123', 'Bucket123', 'PrintName123.ipynb', runtimeToolLabels.Jupyter); expect(watchCreate).toBeCalledTimes(1); expect(watchCreate).toBeCalledWith('MyIpynbContents'); }); it('handles Azure workspace', async () => { // Arrange - const mockAzureStorage: Partial = { - blob: jest.fn(), - }; - const watchCreate = jest.fn(); - asMockedFn((mockAzureStorage as AjaxAzureStorageContract).blob).mockImplementation(() => { - const mockBlobContract: Partial = { - create: watchCreate, - }; - const blobContract = mockBlobContract as AjaxAzureStorageBlobContract; - asMockedFn(blobContract.create).mockResolvedValue(undefined); - return blobContract; - }); + const blob: MockedFn = jest.fn(); + const watchCreate: MockedFn = jest.fn(); + watchCreate.mockResolvedValue(undefined); - const mockAjax: Partial = { - AzureStorage: mockAzureStorage as AjaxAzureStorageContract, - }; - asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract); + blob.mockReturnValue(partial({ create: watchCreate })); + asMockedFn(AzureStorage).mockReturnValue(partial({ blob })); const workspaceInfo: Partial = { workspaceId: 'Workspace123', @@ -240,8 +186,8 @@ describe('AnalysisProvider - createAnalysis', () => { // Assert expect(result).toEqual(undefined); - expect(mockAzureStorage.blob).toBeCalledTimes(1); - expect(mockAzureStorage.blob).toBeCalledWith('Workspace123', 'PrintName123.ipynb'); + expect(blob).toBeCalledTimes(1); + expect(blob).toBeCalledWith('Workspace123', 'PrintName123.ipynb'); expect(watchCreate).toBeCalledTimes(1); expect(watchCreate).toBeCalledWith('MyIpynbContents'); }); @@ -250,23 +196,12 @@ describe('AnalysisProvider - createAnalysis', () => { describe('AnalysisProvider - deleteAnalysis', () => { it('handles GCP workspace', async () => { // Arrange - const mockBuckets: Partial = { - analysis: jest.fn(), - }; - const watchDelete = jest.fn(); - asMockedFn((mockBuckets as AjaxBucketsContract).analysis).mockImplementation(() => { - const mockAnalysisContract: Partial = { - delete: watchDelete, - }; - const analysisContract = mockAnalysisContract as AjaxBucketsAnalysisContract; - asMockedFn(analysisContract.delete).mockResolvedValue(undefined); - return analysisContract; - }); + const analysis: MockedFn = jest.fn(); + const watchDelete: MockedFn = jest.fn(); + watchDelete.mockResolvedValue(undefined); - const mockAjax: Partial = { - Buckets: mockBuckets as AjaxBucketsContract, - }; - asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract); + analysis.mockReturnValue(partial({ delete: watchDelete })); + asMockedFn(GoogleStorage).mockReturnValue(partial({ analysis })); const workspaceInfo: Partial = { googleProject: 'GoogleProject123', @@ -285,35 +220,19 @@ describe('AnalysisProvider - deleteAnalysis', () => { // contents: any, signal?: AbortSignal) => Promise // Assert expect(result).toEqual(undefined); - expect(mockBuckets.analysis).toBeCalledTimes(1); - expect(mockBuckets.analysis).toBeCalledWith( - 'GoogleProject123', - 'Bucket123', - 'PrintName123.ipynb', - runtimeToolLabels.Jupyter - ); + expect(analysis).toBeCalledTimes(1); + expect(analysis).toBeCalledWith('GoogleProject123', 'Bucket123', 'PrintName123.ipynb', runtimeToolLabels.Jupyter); expect(watchDelete).toBeCalledTimes(1); }); it('handles Azure workspace', async () => { // Arrange - const mockAzureStorage: Partial = { - blob: jest.fn(), - }; - const watchDelete = jest.fn(); - asMockedFn((mockAzureStorage as AjaxAzureStorageContract).blob).mockImplementation(() => { - const mockBlobContract: Partial = { - delete: watchDelete, - }; - const blobContract = mockBlobContract as AjaxAzureStorageBlobContract; - asMockedFn(blobContract.delete).mockResolvedValue(undefined); - return blobContract; - }); + const blob: MockedFn = jest.fn(); + const watchDelete: MockedFn = jest.fn(); + watchDelete.mockResolvedValue(undefined); - const mockAjax: Partial = { - AzureStorage: mockAzureStorage as AjaxAzureStorageContract, - }; - asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract); + blob.mockReturnValue(partial({ delete: watchDelete })); + asMockedFn(AzureStorage).mockReturnValue(partial({ blob })); const workspaceInfo: Partial = { workspaceId: 'Workspace123', @@ -328,8 +247,8 @@ describe('AnalysisProvider - deleteAnalysis', () => { // Assert expect(result).toEqual(undefined); - expect(mockAzureStorage.blob).toBeCalledTimes(1); - expect(mockAzureStorage.blob).toBeCalledWith('Workspace123', 'PrintName123.ipynb'); + expect(blob).toBeCalledTimes(1); + expect(blob).toBeCalledWith('Workspace123', 'PrintName123.ipynb'); expect(watchDelete).toBeCalledTimes(1); }); }); diff --git a/src/libs/ajax/leonardo/providers/LeoAppProvider.test.ts b/src/libs/ajax/leonardo/providers/LeoAppProvider.test.ts index 9e9fe68c99..0ebd2415db 100644 --- a/src/libs/ajax/leonardo/providers/LeoAppProvider.test.ts +++ b/src/libs/ajax/leonardo/providers/LeoAppProvider.test.ts @@ -1,13 +1,11 @@ -import { Ajax } from 'src/libs/ajax'; -import { AppAjaxContract, AppsAjaxContract } from 'src/libs/ajax/leonardo/Apps'; -import { asMockedFn } from 'src/testing/test-utils'; +import { AppAjaxContract, Apps, AppsAjaxContract } from 'src/libs/ajax/leonardo/Apps'; +import { asMockedFn, partial } from 'src/testing/test-utils'; import { defaultAzureWorkspace } from 'src/testing/workspace-fixtures'; import { AppBasics, leoAppProvider } from './LeoAppProvider'; -jest.mock('src/libs/ajax'); +jest.mock('src/libs/ajax/leonardo/Apps'); -type AjaxContract = ReturnType; type AppNeeds = Pick; type AppsNeeds = Pick; @@ -16,10 +14,9 @@ interface AjaxMockNeeds { app: AppNeeds; } /** - * local test utility - mocks the Ajax super-object and the subset of needed multi-contracts it - * returns with as much type-safety as possible. + * local test utility - sets up mocks for needed ajax data-calls with as much type-saftely as possible. * - * @return collection of key contract sub-objects for easy + * @return collection of key data-call fns for easy * mock overrides and/or method spying/assertions */ const mockAjaxNeeds = (): AjaxMockNeeds => { @@ -28,18 +25,15 @@ const mockAjaxNeeds = (): AjaxMockNeeds => { pause: jest.fn(), details: jest.fn(), }; - const mockApp = partialApp as AppAjaxContract; const partialApps: AppsNeeds = { - app: jest.fn(), + app: jest.fn(() => partial(partialApp)), listWithoutProject: jest.fn(), deleteAppV2: jest.fn(), getAppV2: jest.fn(), }; - const mockApps = partialApps as AppsAjaxContract; - asMockedFn(mockApps.app).mockReturnValue(mockApp); - asMockedFn(Ajax).mockReturnValue({ Apps: mockApps } as AjaxContract); + asMockedFn(Apps).mockReturnValue(partial(partialApps)); return { Apps: partialApps, @@ -58,8 +52,8 @@ describe('leoAppProvider', () => { const result = await leoAppProvider.listWithoutProject({ arg: '1' }, { signal }); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(signal); + expect(Apps).toBeCalledTimes(1); + expect(Apps).toBeCalledWith(signal); expect(ajaxMock.Apps.listWithoutProject).toBeCalledTimes(1); expect(ajaxMock.Apps.listWithoutProject).toBeCalledWith({ arg: '1' }); expect(result).toEqual([]); @@ -83,8 +77,8 @@ describe('leoAppProvider', () => { void (await leoAppProvider.pause(app, { signal: abort.signal })); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(abort.signal); + expect(Apps).toBeCalledTimes(1); + expect(Apps).toBeCalledWith(abort.signal); expect(ajaxMock.Apps.app).toBeCalledTimes(1); expect(ajaxMock.Apps.app).toBeCalledWith('myGoogleProject', 'myAppName'); expect(ajaxMock.app.pause).toBeCalledTimes(1); @@ -108,8 +102,8 @@ describe('leoAppProvider', () => { void (await leoAppProvider.delete(app, { signal: abort.signal })); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(abort.signal); + expect(Apps).toBeCalledTimes(1); + expect(Apps).toBeCalledWith(abort.signal); expect(ajaxMock.Apps.app).toBeCalledTimes(1); expect(ajaxMock.Apps.app).toBeCalledWith('myGoogleProject', 'myAppName'); expect(ajaxMock.app.delete).toBeCalledTimes(1); @@ -134,8 +128,8 @@ describe('leoAppProvider', () => { void (await leoAppProvider.get(app, { signal: abort.signal })); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(abort.signal); + expect(Apps).toBeCalledTimes(1); + expect(Apps).toBeCalledWith(abort.signal); expect(ajaxMock.Apps.app).toBeCalledTimes(1); expect(ajaxMock.Apps.app).toBeCalledWith('myGoogleProject', 'myAppName'); expect(ajaxMock.app.details).toBeCalledTimes(1); @@ -162,7 +156,7 @@ describe('leoAppProvider', () => { // Assert await expect(shouldThrow()).rejects.toEqual(new Error('Pausing apps is not supported for azure')); - expect(Ajax).toBeCalledTimes(0); + expect(Apps).toBeCalledTimes(0); }); it('handles delete app call', async () => { @@ -182,8 +176,8 @@ describe('leoAppProvider', () => { void (await leoAppProvider.delete(app, { signal: abort.signal })); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(abort.signal); + expect(Apps).toBeCalledTimes(1); + expect(Apps).toBeCalledWith(abort.signal); expect(ajaxMock.Apps.deleteAppV2).toBeCalledTimes(1); expect(ajaxMock.Apps.deleteAppV2).toBeCalledWith(app.appName, app.workspaceId); }); @@ -210,7 +204,7 @@ describe('leoAppProvider', () => { `Deleting apps is currently only supported for azure or google apps. Azure apps must have a workspace id. App: ${app.appName} workspaceId: null` ) ); - expect(Ajax).toBeCalledTimes(0); + expect(Apps).toBeCalledTimes(0); }); it('handles get app call', async () => { @@ -230,8 +224,8 @@ describe('leoAppProvider', () => { void (await leoAppProvider.get(app, { signal: abort.signal })); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(abort.signal); + expect(Apps).toBeCalledTimes(1); + expect(Apps).toBeCalledWith(abort.signal); expect(ajaxMock.Apps.getAppV2).toBeCalledTimes(1); expect(ajaxMock.Apps.getAppV2).toBeCalledWith(app.appName, app.workspaceId); }); @@ -258,7 +252,7 @@ describe('leoAppProvider', () => { `Getting apps is currently only supported for azure or google apps. Azure apps must have a workspace id. App: ${app.appName} workspaceId: null` ) ); - expect(Ajax).toBeCalledTimes(0); + expect(Apps).toBeCalledTimes(0); }); }); }); diff --git a/src/libs/ajax/leonardo/providers/LeoAppProvider.ts b/src/libs/ajax/leonardo/providers/LeoAppProvider.ts index a4af26f935..08087a85c7 100644 --- a/src/libs/ajax/leonardo/providers/LeoAppProvider.ts +++ b/src/libs/ajax/leonardo/providers/LeoAppProvider.ts @@ -1,6 +1,6 @@ import { AbortOption } from '@terra-ui-packages/data-client-core'; import { AppWithWorkspace } from 'src/analysis/Environments/Environments.models'; -import { Ajax } from 'src/libs/ajax'; +import { Apps } from 'src/libs/ajax/leonardo/Apps'; import { GetAppItem, ListAppItem } from 'src/libs/ajax/leonardo/models/app-models'; export type AppBasics = Pick & { @@ -22,7 +22,7 @@ export const leoAppProvider: LeoAppProvider = { listWithoutProject: (listArgs: Record, options: AbortOption = {}): Promise => { const { signal } = options; - return Ajax(signal).Apps.listWithoutProject(listArgs); + return Apps(signal).listWithoutProject(listArgs); }, delete: (app: AppBasics, options: DeleteAppOptions = {}): Promise => { const { cloudProvider, cloudResource } = app.cloudContext; @@ -30,10 +30,10 @@ export const leoAppProvider: LeoAppProvider = { const { appName, workspaceId } = app; if (cloudProvider === 'GCP') { - return Ajax(signal).Apps.app(cloudResource, appName).delete(!!deleteDisk); + return Apps(signal).app(cloudResource, appName).delete(!!deleteDisk); } if (cloudProvider === 'AZURE' && !!workspaceId) { - return Ajax(signal).Apps.deleteAppV2(appName, workspaceId); + return Apps(signal).deleteAppV2(appName, workspaceId); } throw new Error( `Deleting apps is currently only supported for azure or google apps. Azure apps must have a workspace id. App: ${app.appName} workspaceId: ${workspaceId}` @@ -47,7 +47,7 @@ export const leoAppProvider: LeoAppProvider = { throw new Error('Pausing apps is not supported for azure'); } - return Ajax(signal).Apps.app(cloudResource, app.appName).pause(); + return Apps(signal).app(cloudResource, app.appName).pause(); }, get: (app: AppBasics, options: AbortOption = {}): Promise => { const { cloudResource, cloudProvider } = app.cloudContext; @@ -55,10 +55,10 @@ export const leoAppProvider: LeoAppProvider = { const { workspaceId } = app; if (cloudProvider === 'GCP') { - return Ajax(signal).Apps.app(cloudResource, app.appName).details(); + return Apps(signal).app(cloudResource, app.appName).details(); } if (cloudProvider === 'AZURE' && !!workspaceId) { - return Ajax(signal).Apps.getAppV2(app.appName, workspaceId); + return Apps(signal).getAppV2(app.appName, workspaceId); } throw new Error( diff --git a/src/libs/ajax/leonardo/providers/LeoDiskProvider.test.ts b/src/libs/ajax/leonardo/providers/LeoDiskProvider.test.ts index 3d5e600c4c..0c687bb9e4 100644 --- a/src/libs/ajax/leonardo/providers/LeoDiskProvider.test.ts +++ b/src/libs/ajax/leonardo/providers/LeoDiskProvider.test.ts @@ -24,10 +24,9 @@ interface DiskMockNeeds { } /** - * local test utility - mocks the Ajax super-object and the subset of needed multi-contracts it - * returns with as much type-saftely as possible. + * local test utility - sets up mocks for needed ajax data-calls with as much type-saftely as possible. * - * @return collection of key contract sub-objects for easy + * @return collection of key data-call fns for easy * mock overrides and/or method spying/assertions */ const mockDiskNeeds = (): DiskMockNeeds => { diff --git a/src/libs/ajax/leonardo/providers/LeoRuntimeProvider.test.ts b/src/libs/ajax/leonardo/providers/LeoRuntimeProvider.test.ts index 3fe3269e87..ce807b6893 100644 --- a/src/libs/ajax/leonardo/providers/LeoRuntimeProvider.test.ts +++ b/src/libs/ajax/leonardo/providers/LeoRuntimeProvider.test.ts @@ -1,9 +1,9 @@ -import { Ajax } from 'src/libs/ajax'; -import { GoogleStorageContract } from 'src/libs/ajax/GoogleStorage'; +import { GoogleStorage, GoogleStorageContract } from 'src/libs/ajax/GoogleStorage'; import { AsyncRuntimeFields, GetRuntimeItem } from 'src/libs/ajax/leonardo/models/runtime-models'; import { RuntimeAjaxContractV1, RuntimeAjaxContractV2, + Runtimes, RuntimesAjaxContract, RuntimeWrapperAjaxContract, } from 'src/libs/ajax/leonardo/Runtimes'; @@ -11,9 +11,9 @@ import { asMockedFn, partial } from 'src/testing/test-utils'; import { leoRuntimeProvider, RuntimeBasics, RuntimeErrorInfo } from './LeoRuntimeProvider'; -jest.mock('src/libs/ajax'); +jest.mock('src/libs/ajax/GoogleStorage'); +jest.mock('src/libs/ajax/leonardo/Runtimes'); -type AjaxContract = ReturnType; type RuntimesNeeds = Pick; type RuntimeV1Needs = Pick; type RuntimeV2Needs = Pick; @@ -21,81 +21,66 @@ type RuntimeWrapperNeeds = Pick; type BucketsNeeds = Pick; interface AjaxMockNeeds { - Runtimes: RuntimesNeeds; + runtimes: RuntimesNeeds; runtimeV1: RuntimeV1Needs; runtimeV2: RuntimeV2Needs; runtimeWrapper: RuntimeWrapperNeeds; - Buckets: BucketsNeeds; + buckets: BucketsNeeds; } /** - * local test utility - mocks the Ajax super-object and the subset of needed multi-contracts it - * returns with as much type-saftely as possible. + * local test utility - sets up mocks for needed ajax data-calls with as much type-saftely as possible. * - * @return collection of key contract sub-objects for easy + * @return collection of key data-call fns for easy * mock overrides and/or method spying/assertions */ const mockAjaxNeeds = (): AjaxMockNeeds => { - const partialRuntimeV1: RuntimeV1Needs = { + const runtimeV1: RuntimeV1Needs = { delete: jest.fn(), details: jest.fn(), }; - const mockRuntimeV1 = partialRuntimeV1 as RuntimeAjaxContractV1; - - const partialRuntimeV2: RuntimeV2Needs = { + const runtimeV2: RuntimeV2Needs = { delete: jest.fn(), details: jest.fn(), }; - const mockRuntimeV2 = partialRuntimeV2 as RuntimeAjaxContractV2; - - const partialRuntimeWrapper: RuntimeWrapperNeeds = { + const runtimeWrapper: RuntimeWrapperNeeds = { stop: jest.fn(), }; - const mockRuntimeWrapper = partialRuntimeWrapper as RuntimeWrapperAjaxContract; - - // Ajax.Runtimes root - const partialRuntimes: RuntimesNeeds = { + // Ajax.runtimes root + const runtimes: RuntimesNeeds = { listV2: jest.fn(), runtime: jest.fn(), runtimeV2: jest.fn(), runtimeWrapper: jest.fn(), }; - asMockedFn(partialRuntimes.runtime).mockReturnValue(mockRuntimeV1); - asMockedFn(partialRuntimes.runtimeV2).mockReturnValue(mockRuntimeV2); - asMockedFn(partialRuntimes.runtimeWrapper).mockReturnValue(mockRuntimeWrapper); + asMockedFn(runtimes.runtime).mockReturnValue(partial(runtimeV1)); + asMockedFn(runtimes.runtimeV2).mockReturnValue(partial(runtimeV2)); + asMockedFn(runtimes.runtimeWrapper).mockReturnValue(partial(runtimeWrapper)); - const partialBuckets: BucketsNeeds = { + const buckets: BucketsNeeds = { getObjectPreview: jest.fn(), }; - asMockedFn(Ajax).mockReturnValue({ - Runtimes: partialRuntimes, - Buckets: partialBuckets, - } as AjaxContract); - - return { - Runtimes: partialRuntimes, - runtimeV1: partialRuntimeV1, - runtimeV2: partialRuntimeV2, - runtimeWrapper: partialRuntimeWrapper, - Buckets: partialBuckets, - }; + asMockedFn(Runtimes).mockReturnValue(partial(runtimes)); + asMockedFn(GoogleStorage).mockReturnValue(partial(buckets)); + + return { runtimes, runtimeV1, runtimeV2, runtimeWrapper, buckets }; }; describe('leoRuntimeProvider', () => { it('handles list runtimes call', async () => { // Arrange const ajaxMock = mockAjaxNeeds(); - asMockedFn(ajaxMock.Runtimes.listV2).mockResolvedValue([]); + asMockedFn(ajaxMock.runtimes.listV2).mockResolvedValue([]); const signal = new window.AbortController().signal; // Act const result = await leoRuntimeProvider.list({ arg: '1' }, { signal }); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(signal); - expect(ajaxMock.Runtimes.listV2).toBeCalledTimes(1); - expect(ajaxMock.Runtimes.listV2).toBeCalledWith({ arg: '1' }); + expect(Runtimes).toBeCalledTimes(1); + expect(Runtimes).toBeCalledWith(signal); + expect(ajaxMock.runtimes.listV2).toBeCalledTimes(1); + expect(ajaxMock.runtimes.listV2).toBeCalledWith({ arg: '1' }); expect(result).toEqual([]); }); @@ -122,10 +107,10 @@ describe('leoRuntimeProvider', () => { const errorInfo = await leoRuntimeProvider.errorInfo(runtime, { signal: abort.signal }); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(abort.signal); - expect(ajaxMock.Runtimes.runtime).toBeCalledTimes(1); - expect(ajaxMock.Runtimes.runtime).toBeCalledWith('myGoogleProject', 'myRuntime'); + expect(Runtimes).toBeCalledTimes(1); + expect(Runtimes).toBeCalledWith(abort.signal); + expect(ajaxMock.runtimes.runtime).toBeCalledTimes(1); + expect(ajaxMock.runtimes.runtime).toBeCalledWith('myGoogleProject', 'myRuntime'); expect(ajaxMock.runtimeV1.details).toBeCalledTimes(1); expect(errorInfo).toEqual({ @@ -154,7 +139,7 @@ describe('leoRuntimeProvider', () => { errors: [{ errorCode: 123, errorMessage: 'Userscript failed: See bucket for details', timestamp: '0:00' }], }) ); - asMockedFn(ajaxMock.Buckets.getObjectPreview).mockResolvedValue( + asMockedFn(ajaxMock.buckets.getObjectPreview).mockResolvedValue( new Response('Error: MeaningOfLife is undefined') ); @@ -162,13 +147,13 @@ describe('leoRuntimeProvider', () => { const errorInfo = await leoRuntimeProvider.errorInfo(runtime, { signal: abort.signal }); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(abort.signal); - expect(ajaxMock.Runtimes.runtime).toBeCalledTimes(1); - expect(ajaxMock.Runtimes.runtime).toBeCalledWith('myGoogleProject', 'myRuntime'); + expect(Runtimes).toBeCalledTimes(1); + expect(Runtimes).toBeCalledWith(abort.signal); + expect(ajaxMock.runtimes.runtime).toBeCalledTimes(1); + expect(ajaxMock.runtimes.runtime).toBeCalledWith('myGoogleProject', 'myRuntime'); expect(ajaxMock.runtimeV1.details).toBeCalledTimes(1); - expect(ajaxMock.Buckets.getObjectPreview).toBeCalledTimes(1); - expect(ajaxMock.Buckets.getObjectPreview).toBeCalledWith( + expect(ajaxMock.buckets.getObjectPreview).toBeCalledTimes(1); + expect(ajaxMock.buckets.getObjectPreview).toBeCalledWith( 'myGoogleProject', 'myBucket', 'userscript_output.txt', @@ -205,10 +190,10 @@ describe('leoRuntimeProvider', () => { const errorInfo = await leoRuntimeProvider.errorInfo(runtime, { signal: abort.signal }); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(abort.signal); - expect(ajaxMock.Runtimes.runtimeV2).toBeCalledTimes(1); - expect(ajaxMock.Runtimes.runtimeV2).toBeCalledWith('myWorkspace', 'myRuntime'); + expect(Runtimes).toBeCalledTimes(1); + expect(Runtimes).toBeCalledWith(abort.signal); + expect(ajaxMock.runtimes.runtimeV2).toBeCalledTimes(1); + expect(ajaxMock.runtimes.runtimeV2).toBeCalledWith('myWorkspace', 'myRuntime'); expect(ajaxMock.runtimeV2.details).toBeCalledTimes(1); expect(errorInfo).toEqual({ @@ -237,10 +222,10 @@ describe('leoRuntimeProvider', () => { void (await leoRuntimeProvider.stop(runtime, { signal: abort.signal })); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(abort.signal); - expect(ajaxMock.Runtimes.runtimeWrapper).toBeCalledTimes(1); - expect(ajaxMock.Runtimes.runtimeWrapper).toBeCalledWith(runtime); + expect(Runtimes).toBeCalledTimes(1); + expect(Runtimes).toBeCalledWith(abort.signal); + expect(ajaxMock.runtimes.runtimeWrapper).toBeCalledTimes(1); + expect(ajaxMock.runtimes.runtimeWrapper).toBeCalledWith(runtime); expect(ajaxMock.runtimeWrapper.stop).toBeCalledTimes(1); }); @@ -262,10 +247,10 @@ describe('leoRuntimeProvider', () => { void (await leoRuntimeProvider.delete(runtime, { signal: abort.signal })); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(abort.signal); - expect(ajaxMock.Runtimes.runtime).toBeCalledTimes(1); - expect(ajaxMock.Runtimes.runtime).toBeCalledWith('myGoogleProject', 'myRuntime'); + expect(Runtimes).toBeCalledTimes(1); + expect(Runtimes).toBeCalledWith(abort.signal); + expect(ajaxMock.runtimes.runtime).toBeCalledTimes(1); + expect(ajaxMock.runtimes.runtime).toBeCalledWith('myGoogleProject', 'myRuntime'); expect(ajaxMock.runtimeV1.delete).toBeCalledTimes(1); expect(ajaxMock.runtimeV1.delete).toBeCalledWith(false); }); @@ -288,10 +273,10 @@ describe('leoRuntimeProvider', () => { void (await leoRuntimeProvider.delete(runtime, { deleteDisk: true, signal: abort.signal })); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(abort.signal); - expect(ajaxMock.Runtimes.runtimeV2).toBeCalledTimes(1); - expect(ajaxMock.Runtimes.runtimeV2).toBeCalledWith('myWorkspaceId', 'myRuntime'); + expect(Runtimes).toBeCalledTimes(1); + expect(Runtimes).toBeCalledWith(abort.signal); + expect(ajaxMock.runtimes.runtimeV2).toBeCalledTimes(1); + expect(ajaxMock.runtimes.runtimeV2).toBeCalledWith('myWorkspaceId', 'myRuntime'); expect(ajaxMock.runtimeV2.delete).toBeCalledTimes(1); expect(ajaxMock.runtimeV2.delete).toBeCalledWith(true); }); diff --git a/src/libs/ajax/leonardo/providers/LeoRuntimeProvider.ts b/src/libs/ajax/leonardo/providers/LeoRuntimeProvider.ts index bb1b59ca97..caf87153ec 100644 --- a/src/libs/ajax/leonardo/providers/LeoRuntimeProvider.ts +++ b/src/libs/ajax/leonardo/providers/LeoRuntimeProvider.ts @@ -1,8 +1,8 @@ import { AbortOption } from '@terra-ui-packages/data-client-core'; import { isGcpContext } from 'src/analysis/utils/runtime-utils'; -import { Ajax } from 'src/libs/ajax'; +import { GoogleStorage } from 'src/libs/ajax/GoogleStorage'; import { ListRuntimeItem, Runtime, RuntimeError } from 'src/libs/ajax/leonardo/models/runtime-models'; -import { AzureRuntimeWrapper, GoogleRuntimeWrapper, RuntimeWrapper } from 'src/libs/ajax/leonardo/Runtimes'; +import { AzureRuntimeWrapper, GoogleRuntimeWrapper, Runtimes, RuntimeWrapper } from 'src/libs/ajax/leonardo/Runtimes'; export type RuntimeBasics = RuntimeWrapper & Pick; @@ -42,21 +42,23 @@ export const leoRuntimeProvider: LeoRuntimeProvider = { list: (listArgs: Record, options: AbortOption = {}): Promise => { const { signal } = options; - return Ajax(signal).Runtimes.listV2(listArgs); + return Runtimes(signal).listV2(listArgs); }, errorInfo: async (runtime: RuntimeBasics, options?: AbortOption): Promise => { - const ajax = Ajax(options?.signal); + const runtimes = Runtimes(options?.signal); const isGcp = isGcpContext(runtime.cloudContext); const { errors: runtimeErrors, asyncRuntimeFields } = isGcp - ? await ajax.Runtimes.runtime((runtime as GoogleRuntimeWrapper).googleProject, runtime.runtimeName).details() - : await ajax.Runtimes.runtimeV2((runtime as AzureRuntimeWrapper).workspaceId, runtime.runtimeName).details(); + ? await runtimes.runtime((runtime as GoogleRuntimeWrapper).googleProject, runtime.runtimeName).details() + : await runtimes.runtimeV2((runtime as AzureRuntimeWrapper).workspaceId, runtime.runtimeName).details(); if (isGcp && runtimeErrors.some(({ errorMessage }) => errorMessage.includes('Userscript failed'))) { - const scriptErrorDetail = await ajax.Buckets.getObjectPreview( - (runtime as GoogleRuntimeWrapper).googleProject, - asyncRuntimeFields?.stagingBucket, - 'userscript_output.txt', - true - ).then((res) => res.text()); + const scriptErrorDetail = await GoogleStorage(options?.signal) + .getObjectPreview( + (runtime as GoogleRuntimeWrapper).googleProject, + asyncRuntimeFields?.stagingBucket, + 'userscript_output.txt', + true + ) + .then((res) => res.text()); return { errorType: 'UserScriptError', detail: scriptErrorDetail }; } // else return { errorType: 'ErrorList', errors: runtimeErrors }; @@ -66,10 +68,10 @@ export const leoRuntimeProvider: LeoRuntimeProvider = { // TODO: refactor runtimeWrapper and related v1/v2 mechanics into this provider. // This provider largely does the same abstraction pattern that runtimeWrapper is going for. - // We should follow-up and see about removing runtimeWrapper from Ajax.Runtimes and point all current + // We should follow-up and see about removing runtimeWrapper from ajax Runtimes and point all current // consumers to this leoRuntimeProvider instead. Any v1/v2 (GCP/Azure) conditional logic should happen // here, and the Ajax layer should just be mirrors of backend endpoints. - return Ajax(signal).Runtimes.runtimeWrapper(runtime).stop(); + return Runtimes(signal).runtimeWrapper(runtime).stop(); }, delete: (runtime: RuntimeBasics, options: DeleteRuntimeOptions = {}): Promise => { const { cloudContext, runtimeName } = runtime; @@ -77,9 +79,9 @@ export const leoRuntimeProvider: LeoRuntimeProvider = { if (isGcpContext(cloudContext)) { const googleProject = (runtime as GoogleRuntimeWrapper).googleProject; - return Ajax(signal).Runtimes.runtime(googleProject, runtimeName).delete(!!deleteDisk); + return Runtimes(signal).runtime(googleProject, runtimeName).delete(!!deleteDisk); } const workspaceId = (runtime as AzureRuntimeWrapper).workspaceId; - return Ajax(signal).Runtimes.runtimeV2(workspaceId, runtimeName).delete(!!deleteDisk); + return Runtimes(signal).runtimeV2(workspaceId, runtimeName).delete(!!deleteDisk); }, }; diff --git a/src/libs/ajax/workspaces/providers/ExportWorkflowToWorkspaceProvider.test.ts b/src/libs/ajax/workspaces/providers/ExportWorkflowToWorkspaceProvider.test.ts index 94f92f6989..01230a7d36 100644 --- a/src/libs/ajax/workspaces/providers/ExportWorkflowToWorkspaceProvider.test.ts +++ b/src/libs/ajax/workspaces/providers/ExportWorkflowToWorkspaceProvider.test.ts @@ -1,19 +1,19 @@ -import { Ajax, AjaxContract } from 'src/libs/ajax'; -import { MethodsAjaxContract } from 'src/libs/ajax/methods/Methods'; +import { Methods, MethodsAjaxContract } from 'src/libs/ajax/methods/Methods'; +import { Metrics, MetricsContract } from 'src/libs/ajax/Metrics'; import { makeExportWorkflowFromMethodsRepoProvider, makeExportWorkflowFromWorkspaceProvider, } from 'src/libs/ajax/workspaces/providers/ExportWorkflowToWorkspaceProvider'; import { MethodConfiguration, MethodRepoMethod } from 'src/libs/ajax/workspaces/workspace-models'; -import { WorkspacesAjaxContract } from 'src/libs/ajax/workspaces/Workspaces'; -import { asMockedFn } from 'src/testing/test-utils'; +import { WorkspaceContract, Workspaces, WorkspacesAjaxContract } from 'src/libs/ajax/workspaces/Workspaces'; +import { asMockedFn, MockedFn, partial } from 'src/testing/test-utils'; import { WorkspaceInfo } from 'src/workspaces/utils'; -jest.mock('src/libs/ajax'); +jest.mock('src/libs/ajax/Metrics'); +jest.mock('src/libs/ajax/methods/Methods'); +jest.mock('src/libs/ajax/workspaces/Workspaces'); -type WorkspaceAjaxContract = WorkspacesAjaxContract['workspace']; -type MethodConfigAjaxContract = ReturnType['methodConfig']; -type TemplateAjaxContract = MethodsAjaxContract['template']; +type MethodConfigContract = ReturnType; const sourceWorkspace: WorkspaceInfo = { workspaceId: 'Workspace1', @@ -45,26 +45,26 @@ const mockMethodConfiguration: MethodConfiguration = { methodRepoMethod: mockMethodRepoMethod, }; +beforeAll(() => { + asMockedFn(Metrics).mockReturnValue(partial({ captureEvent: jest.fn() })); +}); + describe('export workflow from workspace provider', () => { it('handles export call', async () => { // Arrange - const mockWorkspace: Partial = jest.fn(() => { - return { - methodConfig: mockMethodConfig, - }; - }); + const mockCopyTo: MockedFn = jest.fn(); + const mockWorkspace: MockedFn = jest.fn(); + const mockMethodConfig: MockedFn = jest.fn(); - const mockMethodConfig: Partial = jest.fn(() => { - return { copyTo: mockCopyTo }; - }); + mockMethodConfig.mockReturnValue(partial({ copyTo: mockCopyTo })); + mockWorkspace.mockReturnValue(partial({ methodConfig: mockMethodConfig })); - const mockCopyTo = jest.fn(); + asMockedFn(Workspaces).mockReturnValue( + partial({ + workspace: mockWorkspace, + }) + ); - asMockedFn(Ajax).mockReturnValue({ - Workspaces: { - workspace: mockWorkspace as WorkspaceAjaxContract, - } as WorkspacesAjaxContract, - } as AjaxContract); const signal = new window.AbortController().signal; // Act @@ -77,8 +77,8 @@ describe('export workflow from workspace provider', () => { ); // Assert - expect(Ajax).toHaveBeenCalledTimes(1); - expect(Ajax).toHaveBeenCalledWith(signal); + expect(Workspaces).toHaveBeenCalledTimes(1); + expect(Workspaces).toHaveBeenCalledWith(signal); expect(mockWorkspace).toHaveBeenCalledTimes(1); expect(mockWorkspace).toHaveBeenCalledWith(sourceWorkspace.namespace, sourceWorkspace.name); expect(mockMethodConfig).toHaveBeenCalledTimes(1); @@ -98,31 +98,31 @@ describe('export workflow from workspace provider', () => { describe('export workflow from methods repo provider', () => { it('handles export call', async () => { // Arrange - const mockTemplate: Partial = jest.fn(() => - Promise.resolve({ - rootEntityType: 'shouldberemoved', - inputs: { - shouldBeKept: '', - }, + const mockWorkspace: MockedFn = jest.fn(); + const mockImportMethodConfig: MockedFn = jest.fn(); + + mockWorkspace.mockReturnValue(partial({ importMethodConfig: mockImportMethodConfig })); + + asMockedFn(Workspaces).mockReturnValue( + partial({ + workspace: mockWorkspace, }) ); - const mockWorkspace: Partial = jest.fn(() => { - return { - importMethodConfig: mockImportMethodConfig, - }; + const mockTemplate: MockedFn = jest.fn(); + mockTemplate.mockResolvedValue({ + rootEntityType: 'shouldberemoved', + inputs: { + shouldBeKept: '', + }, }); - const mockImportMethodConfig = jest.fn(); + asMockedFn(Methods).mockReturnValue( + partial({ + template: mockTemplate, + }) + ); - asMockedFn(Ajax).mockReturnValue({ - Methods: { - template: mockTemplate as TemplateAjaxContract, - } as MethodsAjaxContract, - Workspaces: { - workspace: mockWorkspace as WorkspaceAjaxContract, - } as WorkspacesAjaxContract, - } as AjaxContract); const signal = new window.AbortController().signal; // Act @@ -131,13 +131,13 @@ describe('export workflow from methods repo provider', () => { }); // Assert - expect(Ajax).toHaveBeenCalledTimes(2); - - expect(Ajax).toHaveBeenNthCalledWith(1, signal); + expect(Workspaces).toHaveBeenCalledTimes(1); + expect(Workspaces).toHaveBeenCalledWith(signal); expect(mockTemplate).toHaveBeenCalledTimes(1); expect(mockTemplate).toHaveBeenCalledWith(mockMethodRepoMethod); - expect(Ajax).toHaveBeenNthCalledWith(2, signal); + expect(Methods).toHaveBeenCalledTimes(1); + expect(Methods).toHaveBeenCalledWith(signal); expect(mockWorkspace).toHaveBeenCalledTimes(1); expect(mockWorkspace).toHaveBeenCalledWith(destWorkspace.namespace, destWorkspace.name); expect(mockImportMethodConfig).toHaveBeenCalledTimes(1); diff --git a/src/libs/ajax/workspaces/providers/ExportWorkflowToWorkspaceProvider.ts b/src/libs/ajax/workspaces/providers/ExportWorkflowToWorkspaceProvider.ts index b79ee3d431..14d5ed785d 100644 --- a/src/libs/ajax/workspaces/providers/ExportWorkflowToWorkspaceProvider.ts +++ b/src/libs/ajax/workspaces/providers/ExportWorkflowToWorkspaceProvider.ts @@ -1,7 +1,8 @@ import { AbortOption } from '@terra-ui-packages/data-client-core'; -import { Ajax } from 'src/libs/ajax'; +import { Methods } from 'src/libs/ajax/methods/Methods'; import { Metrics } from 'src/libs/ajax/Metrics'; import { MethodConfiguration, MethodRepoMethod } from 'src/libs/ajax/workspaces/workspace-models'; +import { Workspaces } from 'src/libs/ajax/workspaces/Workspaces'; import Events, { extractWorkspaceDetails } from 'src/libs/events'; import { WorkspaceInfo } from 'src/workspaces/utils'; @@ -28,8 +29,8 @@ export const makeExportWorkflowFromWorkspaceProvider = ( export: (destWorkspace: WorkspaceInfo, destWorkflowName: string, options: AbortOption = {}) => { const { signal } = options; - return Ajax(signal) - .Workspaces.workspace(currentWorkspace.namespace, currentWorkspace.name) + return Workspaces(signal) + .workspace(currentWorkspace.namespace, currentWorkspace.name) .methodConfig(methodConfig.namespace, methodConfig.name) .copyTo({ destConfigNamespace: destWorkspace.namespace, @@ -60,10 +61,10 @@ export const makeExportWorkflowFromMethodsRepoProvider = ( // Remove placeholder root entity type from template before importing - // the user can select their own on the workflow configuration page - const { rootEntityType, ...template } = await Ajax(signal).Methods.template(sourceMethod); + const { rootEntityType, ...template } = await Methods(signal).template(sourceMethod); - await Ajax(signal) - .Workspaces.workspace(destWorkspace.namespace, destWorkspace.name) + await Workspaces(signal) + .workspace(destWorkspace.namespace, destWorkspace.name) .importMethodConfig({ ...template, name: destWorkflowName, diff --git a/src/libs/ajax/workspaces/providers/WorkspaceProvider.test.ts b/src/libs/ajax/workspaces/providers/WorkspaceProvider.test.ts index f0dbc94d79..2ceb94d7dd 100644 --- a/src/libs/ajax/workspaces/providers/WorkspaceProvider.test.ts +++ b/src/libs/ajax/workspaces/providers/WorkspaceProvider.test.ts @@ -1,12 +1,10 @@ -import { Ajax } from 'src/libs/ajax'; -import { asMockedFn } from 'src/testing/test-utils'; +import { Workspaces, WorkspacesAjaxContract } from 'src/libs/ajax/workspaces/Workspaces'; +import { asMockedFn, partial } from 'src/testing/test-utils'; import { workspaceProvider } from './WorkspaceProvider'; -jest.mock('src/libs/ajax'); +jest.mock('src/libs/ajax/workspaces/Workspaces'); -type AjaxContract = ReturnType; -type WorkspacesAjaxContract = Pick['Workspaces']; type WorkspacesAjaxNeeds = Pick; interface AjaxMockNeeds { @@ -14,23 +12,18 @@ interface AjaxMockNeeds { } /** - * local test utility - mocks the Ajax super-object and the subset of needed multi-contracts it - * returns with as much type-safety as possible. + * local test utility - sets up mocks for needed ajax data-calls with as much type-saftely as possible. * - * @return collection of key contract sub-objects for easy + * @return collection of key data-call fns for easy * mock overrides and/or method spying/assertions */ const mockAjaxNeeds = (): AjaxMockNeeds => { const partialWorkspaces: WorkspacesAjaxNeeds = { list: jest.fn(), }; - const mockWorkspaces = partialWorkspaces as WorkspacesAjaxContract; + asMockedFn(Workspaces).mockReturnValue(partial(partialWorkspaces)); - asMockedFn(Ajax).mockReturnValue({ Workspaces: mockWorkspaces } as AjaxContract); - - return { - workspaces: partialWorkspaces, - }; + return { workspaces: partialWorkspaces }; }; describe('workspacesProvider', () => { it('handles list call', async () => { @@ -43,8 +36,8 @@ describe('workspacesProvider', () => { const result = await workspaceProvider.list(['field1', 'field2'], { stringAttributeMaxLength: 50, signal }); // Assert; - expect(Ajax).toBeCalledTimes(1); - expect(Ajax).toBeCalledWith(signal); + expect(Workspaces).toBeCalledTimes(1); + expect(Workspaces).toBeCalledWith(signal); expect(ajaxMock.workspaces.list).toBeCalledTimes(1); expect(ajaxMock.workspaces.list).toBeCalledWith(['field1', 'field2'], 50); expect(result).toEqual([]); diff --git a/src/libs/ajax/workspaces/providers/WorkspaceProvider.ts b/src/libs/ajax/workspaces/providers/WorkspaceProvider.ts index 42b6af9ad9..bc56ce9120 100644 --- a/src/libs/ajax/workspaces/providers/WorkspaceProvider.ts +++ b/src/libs/ajax/workspaces/providers/WorkspaceProvider.ts @@ -1,5 +1,5 @@ import { AbortOption } from '@terra-ui-packages/data-client-core'; -import { Ajax } from 'src/libs/ajax'; +import { Workspaces } from 'src/libs/ajax/workspaces/Workspaces'; import { WorkspaceWrapper } from 'src/workspaces/utils'; export type FieldsArg = string[]; @@ -16,7 +16,7 @@ export const workspaceProvider: WorkspaceDataProvider = { list: async (fieldsArgs: FieldsArg, options: WorkspaceListOptions): Promise => { const { signal, stringAttributeMaxLength } = options; - const ws = await Ajax(signal).Workspaces.list(fieldsArgs, stringAttributeMaxLength); + const ws = await Workspaces(signal).list(fieldsArgs, stringAttributeMaxLength); return ws; }, }; diff --git a/src/workspaces/common/state/useCloudEnvironmentPolling.test.ts b/src/workspaces/common/state/useCloudEnvironmentPolling.test.ts index 95f5e3285b..b7258ef519 100644 --- a/src/workspaces/common/state/useCloudEnvironmentPolling.test.ts +++ b/src/workspaces/common/state/useCloudEnvironmentPolling.test.ts @@ -18,10 +18,9 @@ interface AjaxMockNeeds { Runtimes: RuntimeMockNeeds; } /** - * local test utility - mocks the Ajax super-object and the subset of needed multi-contracts it - * returns with as much type-safety as possible. + * local test utility - sets up mocks for needed ajax data-calls with as much type-saftely as possible. * - * @return collection of key contract sub-objects for easy + * @return collection of key data-call fns for easy * mock overrides and/or method spying/assertions */ const mockAjaxNeeds = (): AjaxMockNeeds => {