diff --git a/packages/core/App.tsx b/packages/core/App.tsx
index 3a9364da4..197ce2f42 100644
--- a/packages/core/App.tsx
+++ b/packages/core/App.tsx
@@ -14,7 +14,7 @@ import GlobalActionButtonRow from "./components/GlobalActionButtonRow";
import StatusMessage from "./components/StatusMessage";
import TutorialTooltip from "./components/TutorialTooltip";
import QuerySidebar from "./components/QuerySidebar";
-import { FileExplorerServiceBaseUrl } from "./constants";
+import { Environment } from "./constants";
import { interaction, selection } from "./state";
import useLayoutMeasurements from "./hooks/useLayoutMeasurements";
@@ -39,11 +39,11 @@ interface AppProps {
// Localhost: "https://localhost:9081"
// Stage: "http://stg-aics-api.corp.alleninstitute.org"
// From the web (behind load balancer): "/"
- fileExplorerServiceBaseUrl?: string;
+ environment?: Environment;
}
export default function App(props: AppProps) {
- const { fileExplorerServiceBaseUrl = FileExplorerServiceBaseUrl.PRODUCTION } = props;
+ const { environment = Environment.PRODUCTION } = props;
const dispatch = useDispatch();
const hasQuerySelected = useSelector(selection.selectors.hasQuerySelected);
@@ -80,8 +80,12 @@ export default function App(props: AppProps) {
// Set data source base urls
React.useEffect(() => {
- dispatch(interaction.actions.initializeApp(fileExplorerServiceBaseUrl));
- }, [dispatch, fileExplorerServiceBaseUrl]);
+ dispatch(
+ interaction.actions.initializeApp({
+ environment,
+ })
+ );
+ }, [dispatch, environment]);
// Respond to screen size changes
React.useEffect(() => {
diff --git a/packages/core/components/AnnotationFilterForm/test/AnnotationFilterForm.test.tsx b/packages/core/components/AnnotationFilterForm/test/AnnotationFilterForm.test.tsx
index 24ecae207..705b24ee7 100644
--- a/packages/core/components/AnnotationFilterForm/test/AnnotationFilterForm.test.tsx
+++ b/packages/core/components/AnnotationFilterForm/test/AnnotationFilterForm.test.tsx
@@ -10,6 +10,7 @@ import Annotation from "../../../entity/Annotation";
import FileFilter from "../../../entity/FileFilter";
import { initialState, reducer, reduxLogics, interaction, selection } from "../../../state";
import HttpAnnotationService from "../../../services/AnnotationService/HttpAnnotationService";
+import { FESBaseUrl } from "../../../constants";
describe("", () => {
const LISTROW_TESTID_PREFIX = "default-button-";
@@ -31,14 +32,14 @@ describe("", () => {
it("shows all values as unchecked at first", async () => {
// arrange
const responseStub = {
- when: `test/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
+ when: `${FESBaseUrl.TEST}/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
respondWith: {
data: { data: ["a", "b", "c", "d"] },
},
};
const mockHttpClient = createMockHttpClient(responseStub);
const annotationService = new HttpAnnotationService({
- baseUrl: "test",
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
httpClient: mockHttpClient,
});
sandbox.stub(interaction.selectors, "getAnnotationService").returns(annotationService);
@@ -68,14 +69,14 @@ describe("", () => {
it("deselects and selects a value", async () => {
// arrange
const responseStub = {
- when: `test/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
+ when: `${FESBaseUrl.TEST}/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
respondWith: {
data: { data: ["a", "b", "c", "d"] },
},
};
const mockHttpClient = createMockHttpClient(responseStub);
const annotationService = new HttpAnnotationService({
- baseUrl: "test",
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
httpClient: mockHttpClient,
});
sandbox.stub(interaction.selectors, "getAnnotationService").returns(annotationService);
@@ -124,14 +125,14 @@ describe("", () => {
it("naturally sorts values", async () => {
// arrange
const responseStub = {
- when: `test/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
+ when: `${FESBaseUrl.TEST}/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
respondWith: {
data: { data: ["AICS-24", "AICS-0", "aics-32", "aICs-2"] },
},
};
const mockHttpClient = createMockHttpClient(responseStub);
const annotationService = new HttpAnnotationService({
- baseUrl: "test",
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
httpClient: mockHttpClient,
});
sandbox.stub(interaction.selectors, "getAnnotationService").returns(annotationService);
@@ -172,14 +173,14 @@ describe("", () => {
});
const responseStub = {
- when: `test/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
+ when: `${FESBaseUrl.TEST}/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
respondWith: {
data: { data: [true, false] },
},
};
const mockHttpClient = createMockHttpClient(responseStub);
const annotationService = new HttpAnnotationService({
- baseUrl: "test",
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
httpClient: mockHttpClient,
});
@@ -279,14 +280,14 @@ describe("", () => {
it("naturally sorts values", async () => {
// arrange
const responseStub = {
- when: `test/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
+ when: `${FESBaseUrl.TEST}/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
respondWith: {
data: { data: [5, 8, 6.3, -12, 10000000000, 0] },
},
};
const mockHttpClient = createMockHttpClient(responseStub);
const annotationService = new HttpAnnotationService({
- baseUrl: "test",
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
httpClient: mockHttpClient,
});
sandbox.stub(interaction.selectors, "getAnnotationService").returns(annotationService);
@@ -334,14 +335,14 @@ describe("", () => {
it("naturally sorts values", async () => {
// arrange
const responseStub = {
- when: `test/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
+ when: `${FESBaseUrl.TEST}/file-explorer-service/1.0/annotations/${fooAnnotation.name}/values`,
respondWith: {
data: { data: [446582220, 125, 10845000, 86400000] },
},
};
const mockHttpClient = createMockHttpClient(responseStub);
const annotationService = new HttpAnnotationService({
- baseUrl: "test",
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
httpClient: mockHttpClient,
});
sandbox.stub(interaction.selectors, "getAnnotationService").returns(annotationService);
diff --git a/packages/core/components/DirectoryTree/test/DirectoryTree.test.tsx b/packages/core/components/DirectoryTree/test/DirectoryTree.test.tsx
index b8bb860ee..7e32f6a46 100644
--- a/packages/core/components/DirectoryTree/test/DirectoryTree.test.tsx
+++ b/packages/core/components/DirectoryTree/test/DirectoryTree.test.tsx
@@ -19,7 +19,7 @@ import {
import { Provider } from "react-redux";
import { createSandbox } from "sinon";
-import { TOP_LEVEL_FILE_ANNOTATIONS } from "../../../constants";
+import { FESBaseUrl, TOP_LEVEL_FILE_ANNOTATIONS } from "../../../constants";
import Annotation from "../../../entity/Annotation";
import AnnotationName from "../../../entity/Annotation/AnnotationName";
import { FmsFileAnnotation } from "../../../services/FileService";
@@ -53,14 +53,10 @@ describe("", () => {
type: "Text",
});
- const baseUrl = "http://test-aics.corp.alleninstitute.org";
const baseDisplayAnnotations = TOP_LEVEL_FILE_ANNOTATIONS.filter(
(a) => a.name === AnnotationName.FILE_NAME
);
const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: baseUrl,
- },
selection: {
annotationHierarchy: [fooAnnotation.name, barAnnotation.name],
displayAnnotations: [...baseDisplayAnnotations, fooAnnotation, barAnnotation],
@@ -188,9 +184,13 @@ describe("", () => {
},
];
const mockHttpClient = createMockHttpClient(responseStubs);
- const annotationService = new HttpAnnotationService({ baseUrl, httpClient: mockHttpClient });
+ const fileExplorerServiceBaseUrl = FESBaseUrl.TEST;
+ const annotationService = new HttpAnnotationService({
+ fileExplorerServiceBaseUrl: fileExplorerServiceBaseUrl,
+ httpClient: mockHttpClient,
+ });
const fileService = new HttpFileService({
- baseUrl,
+ fileExplorerServiceBaseUrl: fileExplorerServiceBaseUrl,
httpClient: mockHttpClient,
downloadService: new FileDownloadServiceNoop(),
});
@@ -353,9 +353,6 @@ describe("", () => {
it("only includes one filter value per annotation for an annotation within the hierarchy", async () => {
const oneAnnotationDeepState = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: baseUrl,
- },
selection: {
annotationHierarchy: [fooAnnotation.name],
displayAnnotations: [...baseDisplayAnnotations, fooAnnotation, barAnnotation],
diff --git a/packages/core/components/Modal/MetadataManifest/test/MetadataManifest.test.tsx b/packages/core/components/Modal/MetadataManifest/test/MetadataManifest.test.tsx
index fdf28fa6d..c0fed64a2 100644
--- a/packages/core/components/Modal/MetadataManifest/test/MetadataManifest.test.tsx
+++ b/packages/core/components/Modal/MetadataManifest/test/MetadataManifest.test.tsx
@@ -12,6 +12,7 @@ import { Provider } from "react-redux";
import { createSandbox } from "sinon";
import Modal, { ModalType } from "../..";
+import { FESBaseUrl } from "../../../../constants";
import Annotation from "../../../../entity/Annotation";
import FileFilter from "../../../../entity/FileFilter";
import { initialState, interaction, reduxLogics } from "../../../../state";
@@ -19,10 +20,11 @@ import HttpFileService from "../../../../services/FileService/HttpFileService";
import FileDownloadServiceNoop from "../../../../services/FileDownloadService/FileDownloadServiceNoop";
describe("", () => {
- const baseUrl = "test";
+ const fileExplorerServiceBaseUrl = FESBaseUrl.TEST;
+ const environment = "TEST";
const visibleDialogState = mergeState(initialState, {
interaction: {
- fileExplorerServiceBaseUrl: baseUrl,
+ environment: environment,
visibleModal: ModalType.MetadataManifest,
},
});
@@ -35,7 +37,7 @@ describe("", () => {
};
const mockHttpClient = createMockHttpClient(responseStub);
const fileService = new HttpFileService({
- baseUrl,
+ fileExplorerServiceBaseUrl,
httpClient: mockHttpClient,
downloadService: new FileDownloadServiceNoop(),
});
diff --git a/packages/core/components/Modal/SmallScreenWarning/test/SmallScreenWarning.test.tsx b/packages/core/components/Modal/SmallScreenWarning/test/SmallScreenWarning.test.tsx
index 91b76a105..ce446e63d 100644
--- a/packages/core/components/Modal/SmallScreenWarning/test/SmallScreenWarning.test.tsx
+++ b/packages/core/components/Modal/SmallScreenWarning/test/SmallScreenWarning.test.tsx
@@ -8,10 +8,8 @@ import Modal, { ModalType } from "../..";
import { initialState, interaction, reduxLogics } from "../../../../state";
describe("", () => {
- const baseUrl = "test";
const visibleDialogState = mergeState(initialState, {
interaction: {
- fileExplorerServiceBaseUrl: baseUrl,
visibleModal: ModalType.SmallScreenWarning,
},
});
diff --git a/packages/core/constants/index.ts b/packages/core/constants/index.ts
index 283b0b119..929b628d4 100644
--- a/packages/core/constants/index.ts
+++ b/packages/core/constants/index.ts
@@ -5,10 +5,11 @@ import { AnnotationType } from "../entity/AnnotationFormatter";
export const APP_ID = "fms-file-explorer-core";
// Refer to packages/fms-file-explorer-electron/src/main/menu
-export enum FileExplorerServiceBaseUrl {
- LOCALHOST = "http://localhost:9081",
- STAGING = "https://staging.int.allencell.org",
- PRODUCTION = "https://production.int.allencell.org",
+export enum Environment {
+ LOCALHOST = "LOCALHOST",
+ STAGING = "STAGING",
+ PRODUCTION = "PRODUCTION",
+ TEST = "TEST",
}
export const TOP_LEVEL_FILE_ANNOTATIONS = [
@@ -59,3 +60,24 @@ export const THUMBNAIL_SIZE_TO_NUM_COLUMNS = {
};
export const AICS_FMS_DATA_SOURCE_NAME = "AICS FMS";
+
+export enum FESBaseUrl {
+ LOCALHOST = "http://localhost:9081",
+ STAGING = "https://staging.int.allencell.org",
+ PRODUCTION = "https://production.int.allencell.org",
+ TEST = "http://test.int.allencell.org",
+}
+
+export enum MMSBaseUrl {
+ LOCALHOST = "http://localhost:9060",
+ STAGING = "http://stg-aics-api",
+ PRODUCTION = "http://prod-aics-api",
+ TEST = "http://test-aics-api",
+}
+
+export enum LoadBalancerBaseUrl {
+ LOCALHOST = "http://localhost:8080",
+ STAGING = "http://stg-aics.corp.alleninstitute.org",
+ PRODUCTION = "http://aics.corp.alleninstitute.org",
+ TEST = "http://test-aics.corp.alleninstitute.org",
+}
diff --git a/packages/core/entity/FileDetail/index.ts b/packages/core/entity/FileDetail/index.ts
index 83ddc9f4b..0decf2673 100644
--- a/packages/core/entity/FileDetail/index.ts
+++ b/packages/core/entity/FileDetail/index.ts
@@ -1,5 +1,4 @@
import AnnotationName from "../Annotation/AnnotationName";
-import { FileExplorerServiceBaseUrl } from "../../constants";
import { FmsFileAnnotation } from "../../services/FileService";
import { renderZarrThumbnailURL } from "./RenderZarrThumbnailURL";
@@ -194,21 +193,13 @@ export default class FileDetail {
return this.thumbnail;
}
- public getLinkToPlateUI(baseURL: string): string | undefined {
+ public getLinkToPlateUI(labkeyHost: string): string | undefined {
// Grabbing plate barcode
const platebarcode = this.getFirstAnnotationValue(AnnotationName.PLATE_BARCODE);
-
if (!platebarcode) {
return undefined;
}
-
- let labkeyHost = "localhost:9081";
- if (baseURL === FileExplorerServiceBaseUrl.PRODUCTION) {
- labkeyHost = "aics.corp.alleninstitute.org";
- } else if (baseURL === FileExplorerServiceBaseUrl.STAGING) {
- labkeyHost = "stg-aics.corp.alleninstitute.org";
- }
- return `http://${labkeyHost}/labkey/aics_microscopy/AICS/editPlate.view?Barcode=${platebarcode}`;
+ return `${labkeyHost}/labkey/aics_microscopy/AICS/editPlate.view?Barcode=${platebarcode}`;
}
public getAnnotationNameToLinkMap(): { [annotationName: string]: string } {
diff --git a/packages/core/entity/FileSelection/test/FileSelection.test.ts b/packages/core/entity/FileSelection/test/FileSelection.test.ts
index 5a12775a8..ba369db9e 100644
--- a/packages/core/entity/FileSelection/test/FileSelection.test.ts
+++ b/packages/core/entity/FileSelection/test/FileSelection.test.ts
@@ -6,6 +6,7 @@ import FileSet from "../../FileSet";
import NumericRange from "../../NumericRange";
import FileSelection, { FocusDirective } from "..";
+import { FESBaseUrl } from "../../../constants";
import FileDetail from "../../FileDetail";
import FileFilter from "../../FileFilter";
import FuzzyFilter from "../../FileFilter/FuzzyFilter";
@@ -344,7 +345,7 @@ describe("FileSelection", () => {
describe("fetchAllDetails", () => {
it("returns file details for each selected item", async () => {
// Arrange
- const baseUrl = "test";
+ const fileExplorerServiceBaseUrl = FESBaseUrl.TEST;
const queryResult = [];
for (let i = 0; i < 31; i++) {
queryResult.push(i);
@@ -354,13 +355,15 @@ describe("FileSelection", () => {
.slice(1, 31)
.map((detail) => new FileDetail(detail as any));
const httpClient = createMockHttpClient({
- when: `${baseUrl}/${HttpFileService.BASE_FILES_URL}?from=${0}&limit=${31}`,
+ when: `${fileExplorerServiceBaseUrl}/${
+ HttpFileService.BASE_FILES_URL
+ }?from=${0}&limit=${31}`,
respondWith: {
data: { data: queryResult },
},
});
const fileService = new HttpFileService({
- baseUrl,
+ fileExplorerServiceBaseUrl,
httpClient,
downloadService: new FileDownloadServiceNoop(),
});
diff --git a/packages/core/entity/FileSet/index.ts b/packages/core/entity/FileSet/index.ts
index b68fe0b6b..ae744ecf7 100644
--- a/packages/core/entity/FileSet/index.ts
+++ b/packages/core/entity/FileSet/index.ts
@@ -84,7 +84,7 @@ export default class FileSet {
* by using this as the component's `key` attribute.
*/
public get hash() {
- return `${this.toQueryString()}:${this.fileService.baseUrl}`;
+ return `${this.toQueryString()}:${this.fileService.fileExplorerServiceBaseUrl}`;
}
public async fetchTotalCount() {
@@ -210,7 +210,7 @@ export default class FileSet {
public toJSON() {
return {
queryString: this.toQueryString(),
- baseUrl: this.fileService.baseUrl,
+ fileExplorerServiceBaseUrl: this.fileService.fileExplorerServiceBaseUrl,
};
}
diff --git a/packages/core/entity/FileSet/test/FileSet.test.ts b/packages/core/entity/FileSet/test/FileSet.test.ts
index 94851ae0d..702a73a38 100644
--- a/packages/core/entity/FileSet/test/FileSet.test.ts
+++ b/packages/core/entity/FileSet/test/FileSet.test.ts
@@ -3,6 +3,7 @@ import { expect } from "chai";
import { createSandbox } from "sinon";
import FileSet from "../";
+import { FESBaseUrl } from "../../../constants";
import FileFilter from "../../FileFilter";
import FileSort, { SortOrder } from "../../FileSort";
import { makeFileDetailMock } from "../../FileDetail/mocks";
@@ -148,40 +149,40 @@ describe("FileSet", () => {
});
it("turns indicies for requested data into a properly formed pagination query", async () => {
- const baseUrl = "test";
+ const fileExplorerServiceBaseUrl = FESBaseUrl.TEST;
const spec = [
{
- expectedUrl: `${baseUrl}/${HttpFileService.BASE_FILES_URL}?from=1&limit=28`,
+ expectedUrl: `${fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILES_URL}?from=1&limit=28`,
start: 35,
end: 55,
},
{
- expectedUrl: `${baseUrl}/${HttpFileService.BASE_FILES_URL}?from=11&limit=23`,
+ expectedUrl: `${fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILES_URL}?from=11&limit=23`,
start: 256,
end: 274,
},
{
- expectedUrl: `${baseUrl}/${HttpFileService.BASE_FILES_URL}?from=0&limit=6`,
+ expectedUrl: `${fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILES_URL}?from=0&limit=6`,
start: 0,
end: 5,
},
{
- expectedUrl: `${baseUrl}/${HttpFileService.BASE_FILES_URL}?from=1&limit=11`,
+ expectedUrl: `${fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILES_URL}?from=1&limit=11`,
start: 14,
end: 21,
},
{
- expectedUrl: `${baseUrl}/${HttpFileService.BASE_FILES_URL}?from=0&limit=6`,
+ expectedUrl: `${fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILES_URL}?from=0&limit=6`,
start: 2,
end: 5,
},
{
- expectedUrl: `${baseUrl}/${HttpFileService.BASE_FILES_URL}?from=3&limit=4`,
+ expectedUrl: `${fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILES_URL}?from=3&limit=4`,
start: 12,
end: 15,
},
{
- expectedUrl: `${baseUrl}/${HttpFileService.BASE_FILES_URL}?from=0&limit=301`,
+ expectedUrl: `${fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILES_URL}?from=0&limit=301`,
start: 2,
end: 300,
},
@@ -203,7 +204,7 @@ describe("FileSet", () => {
const fileSet = new FileSet({
fileService: new HttpFileService({
httpClient,
- baseUrl,
+ fileExplorerServiceBaseUrl,
downloadService: new FileDownloadServiceNoop(),
}),
});
diff --git a/packages/core/hooks/useFileAccessContextMenu.ts b/packages/core/hooks/useFileAccessContextMenu.ts
index 372c25e18..307b6cad7 100644
--- a/packages/core/hooks/useFileAccessContextMenu.ts
+++ b/packages/core/hooks/useFileAccessContextMenu.ts
@@ -135,7 +135,7 @@ export default (filters?: FileFilter[], onDismiss?: () => void) => {
],
},
},
- ...(isQueryingAicsFms
+ ...(isQueryingAicsFms && !isOnWeb
? [
{
key: "move-to-cache",
diff --git a/packages/core/hooks/useOpenWithMenuItems/index.tsx b/packages/core/hooks/useOpenWithMenuItems/index.tsx
index db11f5d02..7090f1b22 100644
--- a/packages/core/hooks/useOpenWithMenuItems/index.tsx
+++ b/packages/core/hooks/useOpenWithMenuItems/index.tsx
@@ -177,10 +177,9 @@ export default (fileDetails?: FileDetail, filters?: FileFilter[]): IContextualMe
const annotationNameToAnnotationMap = useSelector(
metadata.selectors.getAnnotationNameToAnnotationMap
);
- const fileExplorerServiceBaseUrl = useSelector(
- interaction.selectors.getFileExplorerServiceBaseUrl
- );
+ const loadBalancerBaseUrl = useSelector(interaction.selectors.getLoadBalancerBaseUrl);
+ const plateLink = fileDetails?.getLinkToPlateUI(loadBalancerBaseUrl);
const annotationNameToLinkMap = React.useMemo(
() =>
fileDetails?.annotations
@@ -249,7 +248,6 @@ export default (fileDetails?: FileDetail, filters?: FileFilter[]): IContextualMe
.filter((app) => supportedApps.every((item) => item.key !== app.key))
.sort((a, b) => (a.text || "").localeCompare(b.text || ""));
- const plateLink = fileDetails?.getLinkToPlateUI(fileExplorerServiceBaseUrl);
if (plateLink && isAicsEmployee) {
supportedApps.push({
key: "open-plate-ui",
diff --git a/packages/core/services/AnnotationService/HttpAnnotationService/index.ts b/packages/core/services/AnnotationService/HttpAnnotationService/index.ts
index 3a6d7b649..0fdb9ff19 100644
--- a/packages/core/services/AnnotationService/HttpAnnotationService/index.ts
+++ b/packages/core/services/AnnotationService/HttpAnnotationService/index.ts
@@ -30,7 +30,7 @@ export default class HttpAnnotationService extends HttpServiceBase implements An
* Fetch all annotations.
*/
public async fetchAnnotations(): Promise {
- const requestUrl = `${this.baseUrl}/${HttpAnnotationService.BASE_ANNOTATION_URL}${this.pathSuffix}`;
+ const requestUrl = `${this.fileExplorerServiceBaseUrl}/${HttpAnnotationService.BASE_ANNOTATION_URL}${this.pathSuffix}`;
const response = await this.get(requestUrl);
return [
@@ -45,7 +45,7 @@ export default class HttpAnnotationService extends HttpServiceBase implements An
public async fetchValues(annotation: string): Promise {
// Encode any special characters in the annotation as necessary
const encodedAnnotation = HttpServiceBase.encodeURISection(annotation);
- const requestUrl = `${this.baseUrl}/${HttpAnnotationService.BASE_ANNOTATION_URL}/${encodedAnnotation}/values${this.pathSuffix}`;
+ const requestUrl = `${this.fileExplorerServiceBaseUrl}/${HttpAnnotationService.BASE_ANNOTATION_URL}/${encodedAnnotation}/values${this.pathSuffix}`;
const response = await this.get(requestUrl);
return response.data;
@@ -70,7 +70,7 @@ export default class HttpAnnotationService extends HttpServiceBase implements An
.filter((param) => !!param)
.join("&");
- const requestUrl = `${this.baseUrl}/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_ROOT_URL}${this.pathSuffix}?${queryParams}`;
+ const requestUrl = `${this.fileExplorerServiceBaseUrl}/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_ROOT_URL}${this.pathSuffix}?${queryParams}`;
const response = await this.get(requestUrl);
return response.data;
@@ -91,7 +91,7 @@ export default class HttpAnnotationService extends HttpServiceBase implements An
]
.filter((param) => !!param)
.join("&");
- const requestUrl = `${this.baseUrl}/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_UNDER_PATH_URL}${this.pathSuffix}?${queryParams}`;
+ const requestUrl = `${this.fileExplorerServiceBaseUrl}/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_UNDER_PATH_URL}${this.pathSuffix}?${queryParams}`;
const response = await this.get(requestUrl);
return response.data;
@@ -103,7 +103,7 @@ export default class HttpAnnotationService extends HttpServiceBase implements An
*/
public async fetchAvailableAnnotationsForHierarchy(annotations: string[]): Promise {
const queryParams = this.buildQueryParams(QueryParam.HIERARCHY, [...annotations].sort());
- const requestUrl = `${this.baseUrl}/${HttpAnnotationService.BASE_AVAILABLE_ANNOTATIONS_UNDER_HIERARCHY}${this.pathSuffix}?${queryParams}`;
+ const requestUrl = `${this.fileExplorerServiceBaseUrl}/${HttpAnnotationService.BASE_AVAILABLE_ANNOTATIONS_UNDER_HIERARCHY}${this.pathSuffix}?${queryParams}`;
const response = await this.get(requestUrl);
if (!response.data) {
diff --git a/packages/core/services/AnnotationService/HttpAnnotationService/test/HttpAnnotationService.test.ts b/packages/core/services/AnnotationService/HttpAnnotationService/test/HttpAnnotationService.test.ts
index 5ac7d9111..9d9db25dd 100644
--- a/packages/core/services/AnnotationService/HttpAnnotationService/test/HttpAnnotationService.test.ts
+++ b/packages/core/services/AnnotationService/HttpAnnotationService/test/HttpAnnotationService.test.ts
@@ -2,7 +2,7 @@ import { createMockHttpClient } from "@aics/redux-utils";
import { expect } from "chai";
import { spy } from "sinon";
-import { TOP_LEVEL_FILE_ANNOTATION_NAMES } from "../../../../constants";
+import { TOP_LEVEL_FILE_ANNOTATION_NAMES, FESBaseUrl } from "../../../../constants";
import Annotation from "../../../../entity/Annotation";
import { annotationsJson } from "../../../../entity/Annotation/mocks";
import FileFilter from "../../../../entity/FileFilter";
@@ -12,7 +12,7 @@ import HttpAnnotationService from "..";
describe("HttpAnnotationService", () => {
describe("fetchAnnotations", () => {
const httpClient = createMockHttpClient({
- when: `test/${HttpAnnotationService.BASE_ANNOTATION_URL}`,
+ when: `${FESBaseUrl.TEST}/${HttpAnnotationService.BASE_ANNOTATION_URL}`,
respondWith: {
data: {
data: annotationsJson,
@@ -21,7 +21,10 @@ describe("HttpAnnotationService", () => {
});
it("issues request for all available Annotations", async () => {
- const annotationService = new HttpAnnotationService({ baseUrl: "test", httpClient });
+ const annotationService = new HttpAnnotationService({
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
+ httpClient,
+ });
const annotations = await annotationService.fetchAnnotations();
expect(annotations.length).to.equal(
annotationsJson.length + TOP_LEVEL_FILE_ANNOTATION_NAMES.length
@@ -35,7 +38,7 @@ describe("HttpAnnotationService", () => {
const annotation = "foo";
const values = ["a", "b", "c"];
const httpClient = createMockHttpClient({
- when: `test/${HttpAnnotationService.BASE_ANNOTATION_URL}/${annotation}/values`,
+ when: `${FESBaseUrl.TEST}/${HttpAnnotationService.BASE_ANNOTATION_URL}/${annotation}/values`,
respondWith: {
data: {
data: values,
@@ -43,7 +46,10 @@ describe("HttpAnnotationService", () => {
},
});
- const annotationService = new HttpAnnotationService({ baseUrl: "test", httpClient });
+ const annotationService = new HttpAnnotationService({
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
+ httpClient,
+ });
const actualValues = await annotationService.fetchValues(annotation);
expect(actualValues.length).to.equal(values.length);
expect(actualValues).to.be.deep.equal(values);
@@ -54,7 +60,7 @@ describe("HttpAnnotationService", () => {
it("issues a request for annotation values for the first level of the annotation hierarchy", async () => {
const expectedValues = ["foo", "bar", "baz"];
const httpClient = createMockHttpClient({
- when: `test/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_ROOT_URL}?order=foo`,
+ when: `${FESBaseUrl.TEST}/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_ROOT_URL}?order=foo`,
respondWith: {
data: {
data: expectedValues,
@@ -62,7 +68,10 @@ describe("HttpAnnotationService", () => {
},
});
- const annotationService = new HttpAnnotationService({ baseUrl: "test", httpClient });
+ const annotationService = new HttpAnnotationService({
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
+ httpClient,
+ });
const values = await annotationService.fetchRootHierarchyValues(["foo"], []);
expect(values).to.equal(expectedValues);
});
@@ -71,7 +80,7 @@ describe("HttpAnnotationService", () => {
const expectedValues = ["foo", "bar", "baz"];
const httpClient = createMockHttpClient({
// note order of query params
- when: `test/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_ROOT_URL}?order=z&order=a&order=b&order=c`,
+ when: `${FESBaseUrl.TEST}/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_ROOT_URL}?order=z&order=a&order=b&order=c`,
respondWith: {
data: {
data: expectedValues,
@@ -80,7 +89,10 @@ describe("HttpAnnotationService", () => {
});
const getSpy = spy(httpClient, "get");
- const annotationService = new HttpAnnotationService({ baseUrl: "test", httpClient });
+ const annotationService = new HttpAnnotationService({
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
+ httpClient,
+ });
// first time around
const firstCallRet = await annotationService.fetchRootHierarchyValues(
@@ -105,7 +117,7 @@ describe("HttpAnnotationService", () => {
it("issues a request for annotation values for the first level of the annotation hierarchy with filters", async () => {
const expectedValues = ["foo", "barValue", "baz"];
const httpClient = createMockHttpClient({
- when: `test/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_ROOT_URL}?order=foo&filter=bar=barValue`,
+ when: `${FESBaseUrl.TEST}/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_ROOT_URL}?order=foo&filter=bar=barValue`,
respondWith: {
data: {
data: expectedValues,
@@ -113,7 +125,10 @@ describe("HttpAnnotationService", () => {
},
});
- const annotationService = new HttpAnnotationService({ baseUrl: "test", httpClient });
+ const annotationService = new HttpAnnotationService({
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
+ httpClient,
+ });
const filter = new FileFilter("bar", "barValue");
const values = await annotationService.fetchRootHierarchyValues(["foo"], [filter]);
expect(values).to.equal(expectedValues);
@@ -124,7 +139,7 @@ describe("HttpAnnotationService", () => {
it("issues request for hierarchy values under a specific path within the hierarchy", async () => {
const expectedValues = [1, 2, 3];
const httpClient = createMockHttpClient({
- when: `test/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_UNDER_PATH_URL}?order=foo&order=bar&path=baz`,
+ when: `${FESBaseUrl.TEST}/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_UNDER_PATH_URL}?order=foo&order=bar&path=baz`,
respondWith: {
data: {
data: expectedValues,
@@ -132,7 +147,10 @@ describe("HttpAnnotationService", () => {
},
});
- const annotationService = new HttpAnnotationService({ baseUrl: "test", httpClient });
+ const annotationService = new HttpAnnotationService({
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
+ httpClient,
+ });
const values = await annotationService.fetchHierarchyValuesUnderPath(
["foo", "bar"],
["baz"],
@@ -144,7 +162,7 @@ describe("HttpAnnotationService", () => {
it("issues request for hierarchy values under a specific path within the hierarchy with filters", async () => {
const expectedValues = [1, "barValue", 3];
const httpClient = createMockHttpClient({
- when: `test/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_UNDER_PATH_URL}?order=foo&order=bar&path=baz&filter=bar=barValue`,
+ when: `${FESBaseUrl.TEST}/${HttpAnnotationService.BASE_ANNOTATION_HIERARCHY_UNDER_PATH_URL}?order=foo&order=bar&path=baz&filter=bar=barValue`,
respondWith: {
data: {
data: expectedValues,
@@ -152,7 +170,10 @@ describe("HttpAnnotationService", () => {
},
});
- const annotationService = new HttpAnnotationService({ baseUrl: "test", httpClient });
+ const annotationService = new HttpAnnotationService({
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
+ httpClient,
+ });
const filter = new FileFilter("bar", "barValue");
const values = await annotationService.fetchHierarchyValuesUnderPath(
["foo", "bar"],
@@ -167,7 +188,7 @@ describe("HttpAnnotationService", () => {
it("issues request for annotations that can be combined with current hierarchy", async () => {
const annotationsFromServer = ["cell_dead", "date_created"];
const httpClient = createMockHttpClient({
- when: `test/${HttpAnnotationService.BASE_ANNOTATION_URL}/hierarchy/available?hierarchy=cas9&hierarchy=cell_line`,
+ when: `${FESBaseUrl.TEST}/${HttpAnnotationService.BASE_ANNOTATION_URL}/hierarchy/available?hierarchy=cas9&hierarchy=cell_line`,
respondWith: {
data: {
data: annotationsFromServer,
@@ -181,7 +202,10 @@ describe("HttpAnnotationService", () => {
...annotationsFromServer,
...hierarchy,
];
- const annotationService = new HttpAnnotationService({ baseUrl: "test", httpClient });
+ const annotationService = new HttpAnnotationService({
+ fileExplorerServiceBaseUrl: FESBaseUrl.TEST,
+ httpClient,
+ });
const values = await annotationService.fetchAvailableAnnotationsForHierarchy(hierarchy);
expect(values.sort()).to.deep.equal(expectedValues.sort());
});
diff --git a/packages/core/services/FileService/HttpFileService/index.ts b/packages/core/services/FileService/HttpFileService/index.ts
index f352cfe2e..ba29e972f 100644
--- a/packages/core/services/FileService/HttpFileService/index.ts
+++ b/packages/core/services/FileService/HttpFileService/index.ts
@@ -20,9 +20,11 @@ interface Config extends ConnectionConfig {
* Service responsible for fetching file related metadata.
*/
export default class HttpFileService extends HttpServiceBase implements FileService {
+ private static readonly CACHE_ENDPOINT_VERSION = "v3.0";
private static readonly ENDPOINT_VERSION = "3.0";
public static readonly BASE_FILES_URL = `file-explorer-service/${HttpFileService.ENDPOINT_VERSION}/files`;
public static readonly BASE_FILE_COUNT_URL = `${HttpFileService.BASE_FILES_URL}/count`;
+ public static readonly BASE_FILE_CACHE_URL = `fss2/${HttpFileService.CACHE_ENDPOINT_VERSION}/file/cache`;
public static readonly SELECTION_AGGREGATE_URL = `${HttpFileService.BASE_FILES_URL}/selection/aggregate`;
private static readonly CSV_ENDPOINT_VERSION = "2.0";
public static readonly BASE_CSV_DOWNLOAD_URL = `file-explorer-service/${HttpFileService.CSV_ENDPOINT_VERSION}/files/selection/manifest`;
@@ -38,7 +40,9 @@ export default class HttpFileService extends HttpServiceBase implements FileServ
*/
public async isNetworkAccessible(): Promise {
try {
- await this.get(`${this.baseUrl}/${HttpFileService.BASE_FILE_COUNT_URL}`);
+ await this.get(
+ `${this.fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILE_COUNT_URL}`
+ );
return true;
} catch (error) {
console.error(`Unable to access AICS network ${error}`);
@@ -49,7 +53,7 @@ export default class HttpFileService extends HttpServiceBase implements FileServ
public async getCountOfMatchingFiles(fileSet: FileSet): Promise {
const requestUrl = join(
compact([
- `${this.baseUrl}/${HttpFileService.BASE_FILE_COUNT_URL}${this.pathSuffix}`,
+ `${this.fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILE_COUNT_URL}${this.pathSuffix}`,
fileSet.toQueryString(),
]),
"?"
@@ -70,7 +74,7 @@ export default class HttpFileService extends HttpServiceBase implements FileServ
): Promise {
const selections = fileSelection.toCompactSelectionList();
const postBody: SelectionAggregationRequest = { selections };
- const requestUrl = `${this.baseUrl}/${HttpFileService.SELECTION_AGGREGATE_URL}${this.pathSuffix}`;
+ const requestUrl = `${this.fileExplorerServiceBaseUrl}/${HttpFileService.SELECTION_AGGREGATE_URL}${this.pathSuffix}`;
const response = await this.post(
requestUrl,
@@ -91,7 +95,7 @@ export default class HttpFileService extends HttpServiceBase implements FileServ
public async getFiles(request: GetFilesRequest): Promise {
const { from, limit, fileSet } = request;
- const base = `${this.baseUrl}/${HttpFileService.BASE_FILES_URL}${this.pathSuffix}?from=${from}&limit=${limit}`;
+ const base = `${this.fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILES_URL}${this.pathSuffix}?from=${from}&limit=${limit}`;
const requestUrl = join(compact([base, fileSet.toQueryString()]), "&");
const response = await this.get(requestUrl);
@@ -113,7 +117,7 @@ export default class HttpFileService extends HttpServiceBase implements FileServ
}
const postData = JSON.stringify({ annotations, selections });
- const url = `${this.baseUrl}/${HttpFileService.BASE_CSV_DOWNLOAD_URL}${this.pathSuffix}`;
+ const url = `${this.fileExplorerServiceBaseUrl}/${HttpFileService.BASE_CSV_DOWNLOAD_URL}${this.pathSuffix}`;
const manifest = await this.downloadService.prepareHttpResourceForDownload(url, postData);
const name = `file-manifest-${new Date()}.csv`;
@@ -127,4 +131,29 @@ export default class HttpFileService extends HttpServiceBase implements FileServ
uniqueId()
);
}
+
+ /**
+ * Cache a list of files to NAS cache (VAST) by sending their IDs to FSS.
+ */
+ public async cacheFiles(
+ fileIds: string[],
+ username?: string
+ ): Promise<{ cacheFileStatuses: { [fileId: string]: string } }> {
+ const requestUrl = `${this.loadBalancerBaseUrl}/${HttpFileService.BASE_FILE_CACHE_URL}${this.pathSuffix}`;
+ const requestBody = JSON.stringify({ fileIds });
+ const headers = {
+ "Content-Type": "application/json",
+ "X-User-Id": username || "anonymous",
+ };
+
+ try {
+ const cacheStatuses = await this.rawPut<{
+ cacheFileStatuses: { [fileId: string]: string };
+ }>(requestUrl, requestBody, headers);
+ return cacheStatuses;
+ } catch (error) {
+ console.error("Failed to cache files:", error);
+ throw new Error("Unable to complete the caching request.");
+ }
+ }
}
diff --git a/packages/core/services/FileService/HttpFileService/test/HttpFileService.test.ts b/packages/core/services/FileService/HttpFileService/test/HttpFileService.test.ts
index 4f5691804..adec881d1 100644
--- a/packages/core/services/FileService/HttpFileService/test/HttpFileService.test.ts
+++ b/packages/core/services/FileService/HttpFileService/test/HttpFileService.test.ts
@@ -2,13 +2,15 @@ import { createMockHttpClient } from "@aics/redux-utils";
import { expect } from "chai";
import HttpFileService from "..";
+import { FESBaseUrl, LoadBalancerBaseUrl } from "../../../../constants";
import FileSelection from "../../../../entity/FileSelection";
import FileSet from "../../../../entity/FileSet";
import NumericRange from "../../../../entity/NumericRange";
import FileDownloadServiceNoop from "../../../FileDownloadService/FileDownloadServiceNoop";
describe("HttpFileService", () => {
- const baseUrl = "test";
+ const fileExplorerServiceBaseUrl = FESBaseUrl.TEST;
+ const loadBalancerBaseUrl = LoadBalancerBaseUrl.TEST;
const fileIds = ["abc123", "def456", "ghi789", "jkl012"];
const files = fileIds.map((file_id) => ({
file_id,
@@ -28,7 +30,7 @@ describe("HttpFileService", () => {
it("issues request for files that match given parameters", async () => {
const httpFileService = new HttpFileService({
- baseUrl,
+ fileExplorerServiceBaseUrl: fileExplorerServiceBaseUrl,
httpClient,
downloadService: new FileDownloadServiceNoop(),
});
@@ -48,7 +50,7 @@ describe("HttpFileService", () => {
const totalFileSize = 12424114;
const totalFileCount = 7;
const httpClient = createMockHttpClient({
- when: `${baseUrl}/${HttpFileService.SELECTION_AGGREGATE_URL}`,
+ when: `${fileExplorerServiceBaseUrl}/${HttpFileService.SELECTION_AGGREGATE_URL}`,
respondWith: {
data: {
data: [{ count: totalFileCount, size: totalFileSize }],
@@ -59,7 +61,7 @@ describe("HttpFileService", () => {
it("issues request for aggregated information about given files", async () => {
// Arrange
const fileService = new HttpFileService({
- baseUrl,
+ fileExplorerServiceBaseUrl,
httpClient,
downloadService: new FileDownloadServiceNoop(),
});
@@ -78,25 +80,39 @@ describe("HttpFileService", () => {
});
});
- describe("getCountOfMatchingFiles", () => {
+ describe("cacheFiles", () => {
const httpClient = createMockHttpClient({
- when: `${baseUrl}/${HttpFileService.BASE_FILE_COUNT_URL}`,
+ when: `${loadBalancerBaseUrl}/${HttpFileService.BASE_FILE_CACHE_URL}`,
respondWith: {
data: {
- data: [2],
+ cacheFileStatuses: {
+ abc123: "DOWNLOAD_COMPLETE",
+ def456: "ERROR",
+ },
},
},
});
- it("issues request for count of files matching given parameters", async () => {
+ it("sends file IDs to be cached and returns their statuses", async () => {
+ // Arrange
const fileService = new HttpFileService({
- baseUrl,
+ loadBalancerBaseUrl: loadBalancerBaseUrl,
httpClient,
downloadService: new FileDownloadServiceNoop(),
});
- const fileSet = new FileSet();
- const count = await fileService.getCountOfMatchingFiles(fileSet);
- expect(count).to.equal(2);
+ const fileIds = ["abc123", "def456"];
+ const username = "test.user";
+
+ // Act
+ const response = await fileService.cacheFiles(fileIds, username);
+
+ // Assert
+ expect(response).to.deep.equal({
+ cacheFileStatuses: {
+ abc123: "DOWNLOAD_COMPLETE",
+ def456: "ERROR",
+ },
+ });
});
});
});
diff --git a/packages/core/services/FileService/index.ts b/packages/core/services/FileService/index.ts
index a540f2da4..9b5a53474 100644
--- a/packages/core/services/FileService/index.ts
+++ b/packages/core/services/FileService/index.ts
@@ -39,7 +39,7 @@ export interface Selection {
}
export default interface FileService {
- baseUrl?: string;
+ fileExplorerServiceBaseUrl?: string;
download(
annotations: string[],
selections: Selection[],
diff --git a/packages/core/services/HttpServiceBase/index.ts b/packages/core/services/HttpServiceBase/index.ts
index 1c89ae508..220ec42a9 100644
--- a/packages/core/services/HttpServiceBase/index.ts
+++ b/packages/core/services/HttpServiceBase/index.ts
@@ -2,20 +2,24 @@ import axios, { AxiosInstance } from "axios";
import { Policy } from "cockatiel";
import LRUCache from "lru-cache";
-import { FileExplorerServiceBaseUrl } from "../../constants";
+import { FESBaseUrl, LoadBalancerBaseUrl, MMSBaseUrl } from "../../constants";
import RestServiceResponse from "../../entity/RestServiceResponse";
export interface ConnectionConfig {
applicationVersion?: string;
- baseUrl?: string | keyof typeof FileExplorerServiceBaseUrl;
+ fileExplorerServiceBaseUrl?: FESBaseUrl;
httpClient?: AxiosInstance;
+ loadBalancerBaseUrl?: LoadBalancerBaseUrl;
+ metadataManagementServiceBaseURl?: MMSBaseUrl;
pathSuffix?: string;
userName?: string;
}
export const DEFAULT_CONNECTION_CONFIG = {
- baseUrl: FileExplorerServiceBaseUrl.PRODUCTION,
+ fileExplorerServiceBaseUrl: FESBaseUrl.PRODUCTION,
httpClient: axios.create(),
+ loadBalancerBaseUrl: LoadBalancerBaseUrl.PRODUCTION,
+ metadataManagementServiceBaseURl: MMSBaseUrl.PRODUCTION,
};
const CHARACTER_TO_ENCODING_MAP: { [index: string]: string } = {
@@ -45,7 +49,7 @@ const retry = Policy.handleAll()
});
/**
- * Base class for services that interact with AICS APIs.
+ * Base class for services that interact with APIs.
*/
export default class HttpServiceBase {
/**
@@ -66,7 +70,7 @@ export default class HttpServiceBase {
}
// encode ampersands that do not separate query string components, so first
- // need to separate the query string componenets (which are split by ampersands themselves)
+ // need to separate the query string components (which are split by ampersands themselves)
// handles case like `workflow=R&DExp&cell_line=AICS-46&foo=bar&cTnT%=3.0`
const re = /&(?=(?:[^&])+\=)/g;
const queryStringComponents = queryString.split(re);
@@ -97,8 +101,12 @@ export default class HttpServiceBase {
.join("");
}
- public baseUrl: string | keyof typeof FileExplorerServiceBaseUrl =
- DEFAULT_CONNECTION_CONFIG.baseUrl;
+ public fileExplorerServiceBaseUrl: string =
+ DEFAULT_CONNECTION_CONFIG.fileExplorerServiceBaseUrl;
+ public loadBalancerBaseUrl: string = DEFAULT_CONNECTION_CONFIG.loadBalancerBaseUrl;
+ public metadataManagementServiceBaseURl: string =
+ DEFAULT_CONNECTION_CONFIG.metadataManagementServiceBaseURl;
+
protected httpClient = DEFAULT_CONNECTION_CONFIG.httpClient;
private applicationVersion = "NOT SET";
private userName?: string;
@@ -114,14 +122,22 @@ export default class HttpServiceBase {
this.setUserName(config.userName);
}
- if (config.baseUrl) {
- this.setBaseUrl(config.baseUrl);
+ if (config.fileExplorerServiceBaseUrl) {
+ this.setFileExplorerServiceBaseUrl(config.fileExplorerServiceBaseUrl);
}
if (config.httpClient) {
this.setHttpClient(config.httpClient);
}
+ if (config.loadBalancerBaseUrl) {
+ this.setLoadBalancerBaseUrl(config.loadBalancerBaseUrl);
+ }
+
+ if (config.metadataManagementServiceBaseURl) {
+ this.setMetadataManagementServiceBaseURl(config.metadataManagementServiceBaseURl);
+ }
+
if (config.pathSuffix) {
this.pathSuffix = config.pathSuffix;
}
@@ -186,6 +202,32 @@ export default class HttpServiceBase {
return response.data;
}
+ public async rawPut(
+ url: string,
+ body: string,
+ headers: { [key: string]: string } = {}
+ ): Promise {
+ const encodedUrl = HttpServiceBase.encodeURI(url);
+ const config = { headers: { ...headers } };
+
+ let response;
+ try {
+ // Retry policy wrapped around axios PUT
+ response = await retry.execute(() => this.httpClient.put(encodedUrl, body, config));
+ } catch (err) {
+ if (axios.isAxiosError(err) && err?.response?.data?.message) {
+ throw new Error(JSON.stringify(err.response.data.message));
+ }
+ throw err;
+ }
+
+ if (response.status >= 400 || response.data === undefined) {
+ throw new Error(`Request for ${encodedUrl} failed`);
+ }
+
+ return response.data;
+ }
+
public async post(url: string, body: string): Promise> {
const encodedUrl = HttpServiceBase.encodeURI(url);
const config = { headers: { "Content-Type": "application/json" } };
@@ -239,18 +281,13 @@ export default class HttpServiceBase {
this.setHeaders();
}
- public setUserName(userName: string) {
- this.userName = userName;
- this.setHeaders();
- }
-
- public setBaseUrl(baseUrl: string | keyof typeof FileExplorerServiceBaseUrl) {
- if (this.baseUrl !== baseUrl) {
+ public setFileExplorerServiceBaseUrl(fileExplorerServiceBaseUrl: FESBaseUrl) {
+ if (this.fileExplorerServiceBaseUrl !== fileExplorerServiceBaseUrl) {
// bust cache when base url changes
this.urlToResponseDataCache.reset();
}
- this.baseUrl = baseUrl;
+ this.fileExplorerServiceBaseUrl = fileExplorerServiceBaseUrl;
}
public setHttpClient(client: AxiosInstance) {
@@ -273,4 +310,27 @@ export default class HttpServiceBase {
delete this.httpClient.defaults.headers.common["X-User-Id"];
}
}
+
+ public setLoadBalancerBaseUrl(loadBalancerBaseUrl: LoadBalancerBaseUrl) {
+ if (this.loadBalancerBaseUrl !== loadBalancerBaseUrl) {
+ // bust cache when base url changes
+ this.urlToResponseDataCache.reset();
+ }
+
+ this.loadBalancerBaseUrl = loadBalancerBaseUrl;
+ }
+
+ public setMetadataManagementServiceBaseURl(metadataManagementServiceBaseURl: MMSBaseUrl) {
+ if (this.metadataManagementServiceBaseURl !== metadataManagementServiceBaseURl) {
+ // bust cache when base url changes
+ this.urlToResponseDataCache.reset();
+ }
+
+ this.metadataManagementServiceBaseURl = metadataManagementServiceBaseURl;
+ }
+
+ public setUserName(userName: string) {
+ this.userName = userName;
+ this.setHeaders();
+ }
}
diff --git a/packages/core/services/PersistentConfigService/index.ts b/packages/core/services/PersistentConfigService/index.ts
index 86a02bc2f..e9a0bc62f 100644
--- a/packages/core/services/PersistentConfigService/index.ts
+++ b/packages/core/services/PersistentConfigService/index.ts
@@ -1,5 +1,6 @@
import { AnnotationResponse } from "../../entity/Annotation";
import { Query } from "../../state/selection/actions";
+import { Environment } from "../../constants";
/**
* Keys for the data saved by this service
@@ -13,6 +14,7 @@ export enum PersistedConfigKeys {
UserSelectedApplications = "USER_SELECTED_APPLICATIONS",
Queries = "QUERIES",
RecentAnnotations = "RECENT_ANNOTATIONS",
+ Environment = "ENVIRONMENT",
}
export interface UserSelectedApplication {
@@ -29,6 +31,7 @@ export interface PersistedConfig {
[PersistedConfigKeys.Queries]?: Query[];
[PersistedConfigKeys.RecentAnnotations]?: string[];
[PersistedConfigKeys.UserSelectedApplications]?: UserSelectedApplication[];
+ [PersistedConfigKeys.Environment]?: Environment;
}
/**
diff --git a/packages/core/state/interaction/actions.ts b/packages/core/state/interaction/actions.ts
index 7f85149c8..eca0a8c54 100644
--- a/packages/core/state/interaction/actions.ts
+++ b/packages/core/state/interaction/actions.ts
@@ -260,12 +260,10 @@ export interface InitializeApp {
payload: string;
}
-export function initializeApp(baseUrl: string): InitializeApp {
- return {
- type: INITIALIZE_APP,
- payload: baseUrl,
- };
-}
+export const initializeApp = (payload: { environment: string }) => ({
+ type: INITIALIZE_APP,
+ payload,
+});
/**
* PROCESS AND STATUS RELATED ENUMS, INTERFACES, ETC.
diff --git a/packages/core/state/interaction/logics.ts b/packages/core/state/interaction/logics.ts
index fd6e3f36c..3bcacc952 100644
--- a/packages/core/state/interaction/logics.ts
+++ b/packages/core/state/interaction/logics.ts
@@ -582,12 +582,22 @@ const setIsSmallScreen = createLogic({
* Logs details of files that are being moved.
*/
const moveFilesLogic = createLogic({
- type: MOVE_FILES,
- process(deps, dispatch, done) {
- const action = deps.action as MoveFilesAction;
- console.log(`Moving files:`, action.payload.fileDetails);
- done();
+ async process({ action, getState }: ReduxLogicDeps, _dispatch, done) {
+ try {
+ const httpFileService = interactionSelectors.getHttpFileService(getState());
+ const username = interactionSelectors.getUserName(getState());
+ const fileIds = (action as MoveFilesAction).payload.fileDetails.map((file) => file.id);
+ const cacheStatuses = await httpFileService.cacheFiles(fileIds, username);
+
+ // TODO: What to do with the status
+ console.log("Cache statuses:", cacheStatuses);
+ } catch (err) {
+ throw new Error(`Error encountered while moving files: ${err}`);
+ } finally {
+ done();
+ }
},
+ type: MOVE_FILES,
});
export default [
diff --git a/packages/core/state/interaction/reducer.ts b/packages/core/state/interaction/reducer.ts
index 2f39ea7eb..735b86632 100644
--- a/packages/core/state/interaction/reducer.ts
+++ b/packages/core/state/interaction/reducer.ts
@@ -30,12 +30,12 @@ import {
} from "./actions";
import { ContextMenuItem, PositionReference } from "../../components/ContextMenu";
import { ModalType } from "../../components/Modal";
+import { Environment } from "../../constants";
import FileFilter from "../../entity/FileFilter";
import { PlatformDependentServices } from "../../services";
import ApplicationInfoServiceNoop from "../../services/ApplicationInfoService/ApplicationInfoServiceNoop";
import FileDownloadServiceNoop from "../../services/FileDownloadService/FileDownloadServiceNoop";
import FileViewerServiceNoop from "../../services/FileViewerService/FileViewerServiceNoop";
-import { DEFAULT_CONNECTION_CONFIG } from "../../services/HttpServiceBase";
import ExecutionEnvServiceNoop from "../../services/ExecutionEnvService/ExecutionEnvServiceNoop";
import { UserSelectedApplication } from "../../services/PersistentConfigService";
import NotificationServiceNoop from "../../services/NotificationService/NotificationServiceNoop";
@@ -51,9 +51,9 @@ export interface InteractionStateBranch {
csvColumns?: string[];
dataSourceInfoForVisibleModal?: DataSourcePromptInfo;
datasetDetailsPanelIsVisible: boolean;
- fileExplorerServiceBaseUrl: string;
fileTypeForVisibleModal: "csv" | "json" | "parquet";
fileFiltersForVisibleModal: FileFilter[];
+ environment: "LOCALHOST" | "STAGING" | "PRODUCTION" | "TEST";
hasDismissedSmallScreenWarning: boolean;
hasUsedApplicationBefore: boolean;
isAicsEmployee?: boolean;
@@ -67,6 +67,7 @@ export interface InteractionStateBranch {
}
export const initialState: InteractionStateBranch = {
+ environment: Environment.PRODUCTION,
contextMenuIsVisible: false,
contextMenuItems: [],
// Passed to `ContextualMenu` as `target`. From the "@fluentui/react" docs:
@@ -75,7 +76,6 @@ export const initialState: InteractionStateBranch = {
// If a MouseEvent is given, the origin point of the event will be used."
contextMenuPositionReference: null,
datasetDetailsPanelIsVisible: false,
- fileExplorerServiceBaseUrl: DEFAULT_CONNECTION_CONFIG.baseUrl,
fileFiltersForVisibleModal: [],
fileTypeForVisibleModal: "csv",
hasDismissedSmallScreenWarning: false,
@@ -166,7 +166,7 @@ export default makeReducer(
}),
[INITIALIZE_APP]: (state, action) => ({
...state,
- fileExplorerServiceBaseUrl: action.payload,
+ environment: action.payload.environment,
}),
[SET_VISIBLE_MODAL]: (state, action) => ({
...state,
diff --git a/packages/core/state/interaction/selectors.ts b/packages/core/state/interaction/selectors.ts
index ec8802285..553de3f46 100644
--- a/packages/core/state/interaction/selectors.ts
+++ b/packages/core/state/interaction/selectors.ts
@@ -14,9 +14,15 @@ import DatabaseFileService from "../../services/FileService/DatabaseFileService"
import HttpAnnotationService from "../../services/AnnotationService/HttpAnnotationService";
import HttpFileService from "../../services/FileService/HttpFileService";
import { ModalType } from "../../components/Modal";
-import { AICS_FMS_DATA_SOURCE_NAME } from "../../constants";
+import {
+ AICS_FMS_DATA_SOURCE_NAME,
+ FESBaseUrl,
+ MMSBaseUrl,
+ LoadBalancerBaseUrl,
+} from "../../constants";
// BASIC SELECTORS
+export const getEnvironment = (state: State) => state.interaction.environment;
export const getContextMenuVisibility = (state: State) => state.interaction.contextMenuIsVisible;
export const getContextMenuItems = (state: State) => state.interaction.contextMenuItems;
export const getContextMenuPositionReference = (state: State) =>
@@ -28,8 +34,6 @@ export const getDataSourceInfoForVisibleModal = (state: State) =>
export const getDatasetDetailsVisibility = (state: State) =>
state.interaction.datasetDetailsPanelIsVisible;
export const getSelectedPublicDataset = (state: State) => state.interaction.selectedPublicDataset;
-export const getFileExplorerServiceBaseUrl = (state: State) =>
- state.interaction.fileExplorerServiceBaseUrl;
export const getFileFiltersForVisibleModal = (state: State) =>
state.interaction.fileFiltersForVisibleModal;
export const getFileTypeForVisibleModal = (state: State) =>
@@ -48,6 +52,22 @@ export const getUserSelectedApplications = (state: State) =>
export const getVisibleModal = (state: State) => state.interaction.visibleModal;
export const isAicsEmployee = (state: State) => state.interaction.isAicsEmployee;
+// URL Mapping Selectors
+export const getFileExplorerServiceBaseUrl = createSelector(
+ [getEnvironment],
+ (environment) => FESBaseUrl[environment]
+);
+
+export const getLoadBalancerBaseUrl = createSelector(
+ [getEnvironment],
+ (environment) => LoadBalancerBaseUrl[environment]
+);
+
+export const getMetadataManagementServiceBaseUrl = createSelector(
+ [getEnvironment],
+ (environment) => MMSBaseUrl[environment]
+);
+
// COMPOSED SELECTORS
export const getApplicationVersion = createSelector(
[getPlatformDependentServices],
@@ -101,16 +121,27 @@ export const getUserName = createSelector(
export const getHttpFileService = createSelector(
[
getApplicationVersion,
- getUserName,
getFileExplorerServiceBaseUrl,
+ getLoadBalancerBaseUrl,
+ getMetadataManagementServiceBaseUrl,
+ getUserName,
getPlatformDependentServices,
getRefreshKey,
],
- (applicationVersion, userName, fileExplorerBaseUrl, platformDependentServices) =>
+ (
+ applicationVersion,
+ fileExplorerServiceBaseUrl,
+ loadBalancerBaseUrl,
+ metadataManagementServiceBaseURL,
+ userName,
+ platformDependentServices
+ ) =>
new HttpFileService({
applicationVersion,
+ fileExplorerServiceBaseUrl: fileExplorerServiceBaseUrl,
+ loadBalancerBaseUrl: loadBalancerBaseUrl,
+ metadataManagementServiceBaseURl: metadataManagementServiceBaseURL,
userName,
- baseUrl: fileExplorerBaseUrl,
downloadService: platformDependentServices.fileDownloadService,
})
);
@@ -161,7 +192,7 @@ export const getAnnotationService = createSelector(
(
applicationVersion,
userName,
- fileExplorerBaseUrl,
+ fileExplorerServiceBaseUrl,
dataSources,
platformDependentServices
): AnnotationService => {
@@ -174,18 +205,18 @@ export const getAnnotationService = createSelector(
return new HttpAnnotationService({
applicationVersion,
userName,
- baseUrl: fileExplorerBaseUrl,
+ fileExplorerServiceBaseUrl: fileExplorerServiceBaseUrl,
});
}
);
export const getDatasetService = createSelector(
[getApplicationVersion, getUserName, getFileExplorerServiceBaseUrl, getRefreshKey],
- (applicationVersion, userName, fileExplorerBaseUrl) =>
+ (applicationVersion, userName, fileExplorerServiceBaseUrl) =>
new DatasetService({
applicationVersion,
userName,
- baseUrl: fileExplorerBaseUrl,
+ fileExplorerServiceBaseUrl: fileExplorerServiceBaseUrl,
})
);
diff --git a/packages/core/state/interaction/test/logics.test.ts b/packages/core/state/interaction/test/logics.test.ts
index 688c55321..f9dc58c19 100644
--- a/packages/core/state/interaction/test/logics.test.ts
+++ b/packages/core/state/interaction/test/logics.test.ts
@@ -24,6 +24,7 @@ import {
} from "../../../services/ExecutionEnvService";
import ExecutionEnvServiceNoop from "../../../services/ExecutionEnvService/ExecutionEnvServiceNoop";
import interactionLogics from "../logics";
+import { FESBaseUrl } from "../../../constants";
import Annotation from "../../../entity/Annotation";
import AnnotationName from "../../../entity/Annotation/AnnotationName";
import { AnnotationType } from "../../../entity/AnnotationFormatter";
@@ -206,7 +207,7 @@ describe("Interaction logics", () => {
it("doesn't use selected files when given a specific file folder path", async () => {
// arrange
- const baseUrl = "test";
+ const fileExplorerServiceBaseUrl = FESBaseUrl.TEST;
const filters = [
new FileFilter("Cell Line", "AICS-12"),
new FileFilter("Notes", "Hello"),
@@ -215,7 +216,6 @@ describe("Interaction logics", () => {
const state = mergeState(initialState, {
interaction: {
fileFiltersForVisibleModal: filters,
- fileExplorerServiceBaseUrl: baseUrl,
platformDependentServices: {
fileDownloadService: new FileDownloadServiceNoop(),
},
@@ -232,7 +232,7 @@ describe("Interaction logics", () => {
};
const mockHttpClient = createMockHttpClient(responseStub);
const fileService = new HttpFileService({
- baseUrl,
+ fileExplorerServiceBaseUrl,
httpClient: mockHttpClient,
downloadService: new FileDownloadServiceNoop(),
});
@@ -776,12 +776,12 @@ describe("Interaction logics", () => {
describe("refresh", () => {
const sandbox = createSandbox();
- const baseUrl = "test";
+ const fileExplorerServiceBaseUrl = FESBaseUrl.TEST;
const annotations = annotationsJson.map((annotation) => new Annotation(annotation));
const availableAnnotations = [annotations[1].displayName];
const responseStubs = [
{
- when: `${baseUrl}/${HttpAnnotationService.BASE_ANNOTATION_URL}`,
+ when: `${fileExplorerServiceBaseUrl}/${HttpAnnotationService.BASE_ANNOTATION_URL}`,
respondWith: {
data: { data: annotations },
},
@@ -798,7 +798,7 @@ describe("Interaction logics", () => {
];
const mockHttpClient = createMockHttpClient(responseStubs);
const annotationService = new HttpAnnotationService({
- baseUrl,
+ fileExplorerServiceBaseUrl,
httpClient: mockHttpClient,
});
@@ -883,7 +883,7 @@ describe("Interaction logics", () => {
],
});
}
- const baseUrl = "test";
+ const fileExplorerServiceBaseUrl = FESBaseUrl.TEST;
const responseStub = {
when: () => true,
respondWith: {
@@ -892,7 +892,7 @@ describe("Interaction logics", () => {
};
const mockHttpClient = createMockHttpClient(responseStub);
const fileService = new HttpFileService({
- baseUrl,
+ fileExplorerServiceBaseUrl,
httpClient: mockHttpClient,
downloadService: new FileDownloadServiceNoop(),
});
@@ -1099,16 +1099,16 @@ describe("Interaction logics", () => {
});
}
const files = [...csvFiles, ...pngFiles];
- const baseUrl = "test";
+ const fileExplorerServiceBaseUrl = FESBaseUrl.TEST;
const responseStub = {
- when: `${baseUrl}/${HttpFileService.BASE_FILES_URL}?from=0&limit=101`,
+ when: `${fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILES_URL}?from=0&limit=101`,
respondWith: {
data: { data: files },
},
};
const mockHttpClient = createMockHttpClient(responseStub);
const fileService = new HttpFileService({
- baseUrl,
+ fileExplorerServiceBaseUrl,
httpClient: mockHttpClient,
downloadService: new FileDownloadServiceNoop(),
});
@@ -1214,16 +1214,16 @@ describe("Interaction logics", () => {
for (let i = 0; i <= 100; i++) {
files.push({ file_path: `/allen/file_${i}.ext` });
}
- const baseUrl = "test";
+ const fileExplorerServiceBaseUrl = FESBaseUrl.TEST;
const responseStub = {
- when: `${baseUrl}/${HttpFileService.BASE_FILES_URL}?from=0&limit=101`,
+ when: `${fileExplorerServiceBaseUrl}/${HttpFileService.BASE_FILES_URL}?from=0&limit=101`,
respondWith: {
data: { data: files },
},
};
const mockHttpClient = createMockHttpClient(responseStub);
const fileService = new HttpFileService({
- baseUrl,
+ fileExplorerServiceBaseUrl,
httpClient: mockHttpClient,
downloadService: new FileDownloadServiceNoop(),
});
diff --git a/packages/core/state/metadata/test/logics.test.ts b/packages/core/state/metadata/test/logics.test.ts
index 9edb3717a..163f2a251 100644
--- a/packages/core/state/metadata/test/logics.test.ts
+++ b/packages/core/state/metadata/test/logics.test.ts
@@ -19,11 +19,7 @@ describe("Metadata logics", () => {
describe("requestAnnotations", () => {
it("Fires RECEIVE_ANNOTATIONS action after processing REQUEST_ANNOTATIONS action", async () => {
// arrange
- const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: "test",
- },
- });
+ const state = mergeState(initialState, {});
const responseStub = {
when: () => true,
diff --git a/packages/core/state/selection/test/logics.test.ts b/packages/core/state/selection/test/logics.test.ts
index edb8a9412..697ec38a5 100644
--- a/packages/core/state/selection/test/logics.test.ts
+++ b/packages/core/state/selection/test/logics.test.ts
@@ -27,6 +27,7 @@ import {
changeSourceMetadata,
} from "../actions";
import { initialState, interaction } from "../../";
+import { FESBaseUrl } from "../../../constants";
import Annotation from "../../../entity/Annotation";
import AnnotationName from "../../../entity/Annotation/AnnotationName";
import FileFilter from "../../../entity/FileFilter";
@@ -330,10 +331,10 @@ describe("Selection logics", () => {
},
},
];
- const baseUrl = "test";
+ const fileExplorerServiceBaseUrl = FESBaseUrl.TEST;
const mockHttpClient = createMockHttpClient(responseStubs);
const fileService = new HttpFileService({
- baseUrl,
+ fileExplorerServiceBaseUrl,
httpClient: mockHttpClient,
downloadService: new FileDownloadServiceNoop(),
});
@@ -354,9 +355,6 @@ describe("Selection logics", () => {
it("selects file above current focused row", async () => {
// Arrange
const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: baseUrl,
- },
selection: {
fileSelection: new FileSelection()
.select({
@@ -398,9 +396,6 @@ describe("Selection logics", () => {
sortOrder: 1,
});
const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: baseUrl,
- },
selection: {
fileSelection: new FileSelection().select({
fileSet,
@@ -462,9 +457,6 @@ describe("Selection logics", () => {
it("adds a new annotation to the end of the hierarchy", async () => {
// setup
const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: "test",
- },
metadata: {
annotations: [...annotations],
},
@@ -494,9 +486,6 @@ describe("Selection logics", () => {
it("moves an annotation within the hierarchy to a new position", async () => {
// setup
const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: "test",
- },
metadata: {
annotations: [...annotations],
},
@@ -540,9 +529,6 @@ describe("Selection logics", () => {
// ones to test proper comparison using annotationName
const annotationHierarchy = annotations.slice(0, 4).map((a) => a.name);
const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: "test",
- },
metadata: {
annotations: [...annotations],
},
@@ -580,9 +566,6 @@ describe("Selection logics", () => {
new FileFolder(["AICS-0", "false"]),
];
const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: "test",
- },
metadata: {
annotations: [...annotations],
},
@@ -612,9 +595,6 @@ describe("Selection logics", () => {
it("determines which paths can still be opened after annotation is added", async () => {
// setup
const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: "test",
- },
metadata: {
annotations: [...annotations],
},
@@ -647,9 +627,6 @@ describe("Selection logics", () => {
it("determines which paths can still be opened after annotation is removed", async () => {
// setup
const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: "test",
- },
metadata: {
annotations: [...annotations],
},
@@ -711,9 +688,6 @@ describe("Selection logics", () => {
it("sets available annotations", async () => {
// Arrange
const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: "test",
- },
metadata: {
annotations: [...annotations],
},
@@ -752,9 +726,6 @@ describe("Selection logics", () => {
it("sets all annotations as available when actual cannot be found", async () => {
// Arrange
const state = mergeState(initialState, {
- interaction: {
- fileExplorerServiceBaseUrl: "test",
- },
metadata: {
annotations: [...annotations],
},
diff --git a/packages/core/state/selection/test/reducer.test.ts b/packages/core/state/selection/test/reducer.test.ts
index ac0c1776b..155b83a17 100644
--- a/packages/core/state/selection/test/reducer.test.ts
+++ b/packages/core/state/selection/test/reducer.test.ts
@@ -16,7 +16,9 @@ import { DataSource } from "../../../services/DataSourceService";
describe("Selection reducer", () => {
[
selection.actions.setAnnotationHierarchy([]),
- interaction.actions.initializeApp("base"),
+ interaction.actions.initializeApp({
+ environment: "TEST",
+ }),
].forEach((expectedAction) =>
it(`clears selected file state when ${expectedAction.type} is fired`, () => {
// arrange
diff --git a/packages/desktop/src/main/global.d.ts b/packages/desktop/src/main/global.d.ts
index 5a194e88c..cbf699645 100644
--- a/packages/desktop/src/main/global.d.ts
+++ b/packages/desktop/src/main/global.d.ts
@@ -1,4 +1,7 @@
/*eslint no-var: "off"*/
-// necessary in order to do: global.fileExplorerServiceBaseUrl = "..."
-declare var fileDownloadServiceBaseUrl: string;
-declare var fileExplorerServiceBaseUrl: string;
+// necessary in order to do: global.environment = "..."
+import { Environment } from "./util/constants";
+
+declare global {
+ var environment: Environment;
+}
diff --git a/packages/desktop/src/main/menu/data-source.ts b/packages/desktop/src/main/menu/data-source.ts
index 979463080..930575fa3 100644
--- a/packages/desktop/src/main/menu/data-source.ts
+++ b/packages/desktop/src/main/menu/data-source.ts
@@ -1,14 +1,9 @@
import { MenuItemConstructorOptions } from "electron";
-import {
- GlobalVariableChannels,
- FileDownloadServiceBaseUrl,
- FileExplorerServiceBaseUrl,
-} from "../../util/constants";
+import { GlobalVariableChannels, Environment } from "../../util/constants";
// Least effort state management accessible to both the main and renderer processes.
-global.fileDownloadServiceBaseUrl = FileDownloadServiceBaseUrl.PRODUCTION;
-global.fileExplorerServiceBaseUrl = FileExplorerServiceBaseUrl.PRODUCTION;
+global.environment = Environment.PRODUCTION;
const dataSourceMenu: MenuItemConstructorOptions = {
label: "Data Source",
@@ -16,12 +11,12 @@ const dataSourceMenu: MenuItemConstructorOptions = {
{
label: "Localhost",
type: "radio",
- checked: global.fileExplorerServiceBaseUrl === FileExplorerServiceBaseUrl.LOCALHOST,
+ checked: global.environment === Environment.LOCALHOST,
click: (_, focusedWindow) => {
if (focusedWindow) {
+ global.environment = Environment.LOCALHOST;
focusedWindow.webContents.send(GlobalVariableChannels.BaseUrl, {
- fileExplorerServiceBaseUrl: FileExplorerServiceBaseUrl.LOCALHOST,
- fileDownloadServiceBaseUrl: FileDownloadServiceBaseUrl.LOCALHOST,
+ environment: Environment.LOCALHOST,
});
}
},
@@ -29,12 +24,12 @@ const dataSourceMenu: MenuItemConstructorOptions = {
{
label: "Staging",
type: "radio",
- checked: global.fileExplorerServiceBaseUrl === FileExplorerServiceBaseUrl.STAGING,
+ checked: global.environment === Environment.STAGING,
click: (_, focusedWindow) => {
if (focusedWindow) {
+ global.environment = Environment.STAGING;
focusedWindow.webContents.send(GlobalVariableChannels.BaseUrl, {
- fileExplorerServiceBaseUrl: FileExplorerServiceBaseUrl.STAGING,
- fileDownloadServiceBaseUrl: FileDownloadServiceBaseUrl.STAGING,
+ environment: Environment.STAGING,
});
}
},
@@ -42,12 +37,12 @@ const dataSourceMenu: MenuItemConstructorOptions = {
{
label: "Production",
type: "radio",
- checked: global.fileExplorerServiceBaseUrl === FileExplorerServiceBaseUrl.PRODUCTION,
+ checked: global.environment === Environment.PRODUCTION,
click: (_, focusedWindow) => {
if (focusedWindow) {
+ global.environment = Environment.PRODUCTION;
focusedWindow.webContents.send(GlobalVariableChannels.BaseUrl, {
- fileExplorerServiceBaseUrl: FileExplorerServiceBaseUrl.PRODUCTION,
- fileDownloadServiceBaseUrl: FileDownloadServiceBaseUrl.PRODUCTION,
+ environment: Environment.PRODUCTION,
});
}
},
diff --git a/packages/desktop/src/renderer/index.tsx b/packages/desktop/src/renderer/index.tsx
index 9cc4afaa4..5ceded4e7 100644
--- a/packages/desktop/src/renderer/index.tsx
+++ b/packages/desktop/src/renderer/index.tsx
@@ -34,12 +34,20 @@ const KeyDownHandler: React.FC<{ clearStore: () => void }> = ({ clearStore }) =>
return null;
};
-// Function to clear the persistent store
+// Clears the persistent store but retains `Environment` to prevent misalignment
+// between the data source and the selected menu item in the app.
const clearPersistentStore = () => {
+ const currentEnvironment = global.environment;
persistentConfigService.clear();
+ persistentConfigService.persist({ [PersistedConfigKeys.Environment]: currentEnvironment });
window.location.reload();
};
+const initializeEnvironment = () => {
+ const savedEnvironment = persistentConfigService.get(PersistedConfigKeys.Environment);
+ global.environment = savedEnvironment || "PRODUCTION";
+};
+
// Application analytics/metrics
const frontendInsights = new FrontendInsights(
{
@@ -120,20 +128,19 @@ function renderFmsFileExplorer() {
-
+
,
document.getElementById(APP_ID)
);
}
-ipcRenderer.addListener(
- GlobalVariableChannels.BaseUrl,
- (_, { fileExplorerServiceBaseUrl, fileDownloadServiceBaseUrl }) => {
- global.fileDownloadServiceBaseUrl = fileDownloadServiceBaseUrl;
- global.fileExplorerServiceBaseUrl = fileExplorerServiceBaseUrl;
- renderFmsFileExplorer();
- }
-);
+// Listen for IPC updates to global variables
+ipcRenderer.addListener(GlobalVariableChannels.BaseUrl, (_, { environment }) => {
+ global.environment = environment;
+ persistentConfigService.persist({ [PersistedConfigKeys.Environment]: environment });
+ renderFmsFileExplorer();
+});
+initializeEnvironment();
renderFmsFileExplorer();
diff --git a/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts b/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts
index 7d1c8efa3..c547a758f 100644
--- a/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts
+++ b/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts
@@ -1,7 +1,7 @@
import { expect } from "chai";
import { PersistedConfigKeys } from "../../../../core/services";
-import { RUN_IN_RENDERER } from "../../util/constants";
+import { Environment, RUN_IN_RENDERER } from "../../util/constants";
import PersistentConfigServiceElectron from "../PersistentConfigServiceElectron";
describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => {
@@ -61,6 +61,7 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => {
units: "string",
},
];
+ const expectedEnvironment = Environment.TEST;
service.persist(PersistedConfigKeys.AllenMountPoint, expectedAllenMountPoint);
service.persist(PersistedConfigKeys.CsvColumns, expectedCsvColumns);
@@ -73,6 +74,7 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => {
service.persist(PersistedConfigKeys.UserSelectedApplications, expectedUserSelectedApps);
service.persist(PersistedConfigKeys.DisplayAnnotations, expectedDisplayAnnotations);
service.persist(PersistedConfigKeys.RecentAnnotations, expectedRecentAnnotations);
+ service.persist(PersistedConfigKeys.Environment, expectedEnvironment);
const expectedConfig = {
[PersistedConfigKeys.AllenMountPoint]: expectedAllenMountPoint,
@@ -83,6 +85,7 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => {
[PersistedConfigKeys.UserSelectedApplications]: expectedUserSelectedApps,
[PersistedConfigKeys.DisplayAnnotations]: expectedDisplayAnnotations,
[PersistedConfigKeys.RecentAnnotations]: expectedRecentAnnotations,
+ [PersistedConfigKeys.Environment]: expectedEnvironment,
};
// Act
@@ -120,6 +123,7 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => {
units: "string",
},
],
+ [PersistedConfigKeys.Environment]: Environment.TEST,
};
// Act
@@ -159,10 +163,13 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => {
service.persist(PersistedConfigKeys.CsvColumns, ["Cell Line"]);
service.persist(PersistedConfigKeys.CsvColumns, expected);
service.persist(PersistedConfigKeys.AllenMountPoint, "/my/path/allen");
+ service.persist(PersistedConfigKeys.Environment, Environment.TEST);
// Assert
const actual = service.get(PersistedConfigKeys.CsvColumns);
+ const actualEnvironment = service.get(PersistedConfigKeys.Environment);
expect(actual).to.be.deep.equal(expected);
+ expect(actualEnvironment).to.equal(Environment.TEST);
});
});
});
diff --git a/packages/desktop/src/util/constants.ts b/packages/desktop/src/util/constants.ts
index 2b950921b..bda836ed7 100644
--- a/packages/desktop/src/util/constants.ts
+++ b/packages/desktop/src/util/constants.ts
@@ -3,16 +3,11 @@
// pattern used in the npm script used to invoke electron-mocha.
export const RUN_IN_RENDERER = "@renderer";
-export enum FileDownloadServiceBaseUrl {
- LOCALHOST = "http://localhost:8080/labkey/fmsfiles/image",
- STAGING = "http://stg-aics.corp.alleninstitute.org/labkey/fmsfiles/image",
- PRODUCTION = "http://aics.corp.alleninstitute.org/labkey/fmsfiles/image",
-}
-
-export enum FileExplorerServiceBaseUrl {
- LOCALHOST = "http://localhost:9081",
- STAGING = "https://staging.int.allencell.org",
- PRODUCTION = "https://production.int.allencell.org",
+export enum Environment {
+ LOCALHOST = "LOCALHOST",
+ STAGING = "STAGING",
+ PRODUCTION = "PRODUCTION",
+ TEST = "TEST",
}
// Channels global variables can be modified on / listen to