Skip to content

Commit

Permalink
UIE-204 Narrow Ajax Usage pt24
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
msilva-broad committed Jan 31, 2025
1 parent 5521b69 commit b5cdab3
Show file tree
Hide file tree
Showing 13 changed files with 154 additions and 175 deletions.
3 changes: 3 additions & 0 deletions src/appLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -21,6 +22,8 @@ RModal.defaultStyles = { overlay: {}, content: {} };

window._ = _;

setupAjaxTestUtil();

initializeAuthListeners();
initializeAuthMetrics();
initAuthTesting();
Expand Down
44 changes: 44 additions & 0 deletions src/libs/ajax.test.ts
Original file line number Diff line number Diff line change
@@ -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<AppsAjaxContract>({}));
asMockedFn(Runtimes).mockReturnValue(partial<RuntimesAjaxContract>({}));
asMockedFn(Workspaces).mockReturnValue(partial<WorkspacesAjaxContract>({}));
});

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);
});
});
60 changes: 6 additions & 54 deletions src/libs/ajax.ts
Original file line number Diff line number Diff line change
@@ -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<typeof Ajax>;
export type AjaxTestingContract = ReturnType<typeof AjaxTestingRoot>;

// 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;
};
4 changes: 2 additions & 2 deletions src/libs/ajax/AzureStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<StorageContainerInfo> => {
const data = await Ajax(signal).WorkspaceManagerResources.controlledResources(workspaceId);
const data = await WorkspaceManagerResources(signal).controlledResources(workspaceId);
const container = _.find(
{
metadata: {
Expand Down
3 changes: 1 addition & 2 deletions src/libs/ajax/SamResources.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -34,7 +33,7 @@ export const SamResources = (signal?: AbortSignal) => ({
object: string,
requesterPaysProject: RequesterPaysProject = undefined
): Promise<string> => {
return Ajax(signal).SamResources.getRequesterPaysSignedUrl(`gs://${bucket}/${object}`, requesterPaysProject);
return SamResources(signal).getRequesterPaysSignedUrl(`gs://${bucket}/${object}`, requesterPaysProject);
},

getResourcePolicies: async (fqResourceId: FullyQualifiedResourceId): Promise<object> => {
Expand Down
6 changes: 3 additions & 3 deletions src/libs/ajax/compute-image-providers/ComputeImageProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -72,8 +72,8 @@ const normalizeImage: (rawImage: ComputeImageRaw) => ComputeImage = (rawImage) =

export const ComputeImageProvider: ComputeImageProviderContract = {
listImages: async (googleProject: string, signal?: AbortSignal): Promise<ComputeImage[]> => {
const fetchedImages: ComputeImageRaw[] = await Ajax(signal)
.Buckets.getObjectPreview(
const fetchedImages: ComputeImageRaw[] = await GoogleStorage(signal)
.getObjectPreview(
googleProject,
getConfig().terraDockerImageBucket,
getConfig().terraDockerVersionsFile,
Expand Down
79 changes: 36 additions & 43 deletions src/libs/ajax/data-table-providers/WdsDataTableProvider.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<ReactNotificationsComponentExports> => {
Expand Down Expand Up @@ -97,12 +97,8 @@ const queryOptions: EntityQueryOptions = {
columnFilter: '',
};

type WorkspaceDataContract = ReturnType<typeof WorkspaceData>;
type AjaxContract = ReturnType<typeof Ajax>;
type AppsContract = ReturnType<typeof Apps>;

describe('WdsDataTableProvider', () => {
const getRecordsMockImpl: WorkspaceDataContract['getRecords'] = (
const getRecordsMockImpl: WorkspaceDataAjaxContract['getRecords'] = (
_root: string,
_instanceId: string,
_recordType: string,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -190,37 +186,34 @@ describe('WdsDataTableProvider', () => {
return Promise.resolve(testProxyUrlResponse);
};

let getRecords: jest.MockedFunction<WorkspaceDataContract['getRecords']>;
let updateRecord: jest.MockedFunction<WorkspaceDataContract['updateRecord']>;
let getCapabilities: jest.MockedFunction<WorkspaceDataContract['getCapabilities']>;
let deleteTable: jest.MockedFunction<WorkspaceDataContract['deleteTable']>;
let downloadTsv: jest.MockedFunction<WorkspaceDataContract['downloadTsv']>;
let uploadTsv: jest.MockedFunction<WorkspaceDataContract['uploadTsv']>;
let listAppsV2: jest.MockedFunction<AppsContract['listAppsV2']>;
const getRecords: MockedFn<WorkspaceDataAjaxContract['getRecords']> = jest.fn();
const updateRecord: MockedFn<WorkspaceDataAjaxContract['updateRecord']> = jest.fn();
const getCapabilities: MockedFn<WorkspaceDataAjaxContract['getCapabilities']> = jest.fn();
const deleteTable: MockedFn<WorkspaceDataAjaxContract['deleteTable']> = jest.fn();
const downloadTsv: MockedFn<WorkspaceDataAjaxContract['downloadTsv']> = jest.fn();
const uploadTsv: MockedFn<WorkspaceDataAjaxContract['uploadTsv']> = jest.fn();
const listAppsV2: MockedFn<AppsAjaxContract['listAppsV2']> = 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<WorkspaceDataContract>,
Apps: { listAppsV2 } as Partial<AppsContract>,
} as Partial<AjaxContract> 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<WorkspaceDataAjaxContract>({
getRecords,
updateRecord,
getCapabilities,
deleteTable,
downloadTsv,
uploadTsv,
})
);
asMockedFn(Apps).mockReturnValue(partial<AppsAjaxContract>({ listAppsV2 }));
});

describe('transformAttributes', () => {
Expand Down
16 changes: 8 additions & 8 deletions src/libs/ajax/data-table-providers/WdsDataTableProvider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import _ from 'lodash/fp';
import { Ajax } from 'src/libs/ajax';
import {
AttributeArray,
DataTableFeatures,
Expand All @@ -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';
Expand Down Expand Up @@ -283,7 +283,7 @@ export class WdsDataTableProvider implements DataTableProvider {
metadata: EntityMetadata
): Promise<EntityQueryResponse> => {
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,
Expand All @@ -301,17 +301,17 @@ export class WdsDataTableProvider implements DataTableProvider {

deleteTable = (entityType: string): Promise<Response> => {
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<Response> => {
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<Blob> => {
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<TsvUploadResponse> => {
Expand All @@ -331,7 +331,7 @@ export class WdsDataTableProvider implements DataTableProvider {
);
}
}, 1000);
return Ajax().WorkspaceData.uploadTsv(
return WorkspaceData().uploadTsv(
this.proxyUrl,
uploadParams.workspaceId,
uploadParams.recordType,
Expand All @@ -342,7 +342,7 @@ export class WdsDataTableProvider implements DataTableProvider {
updateRecord = (recordEditParams: RecordEditParameters): Promise<RecordResponseBody> => {
if (!this.proxyUrl) return Promise.reject('Proxy Url not loaded');

return Ajax().WorkspaceData.updateRecord(
return WorkspaceData().updateRecord(
this.proxyUrl,
recordEditParams.instance,
recordEditParams.recordName,
Expand All @@ -353,7 +353,7 @@ export class WdsDataTableProvider implements DataTableProvider {

updateAttribute = (params: UpdateAttributeParameters): Promise<Blob> => {
if (!this.proxyUrl) return Promise.reject('Proxy Url not loaded');
return Ajax().WorkspaceData.updateAttribute(
return WorkspaceData().updateAttribute(
this.proxyUrl,
this.workspaceId,
params.entityType,
Expand Down
Loading

0 comments on commit b5cdab3

Please sign in to comment.