Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,67 @@ import { UcscTrack } from "../../../../../../../../../../../../utils/ucsc-tracks
import { COLUMN_TYPE } from "../../SampleSheetClassificationStep/types";
import { Strandedness } from "../../StrandednessStep/types";

export interface ConfiguredValue {
// Base configured values shared across all scopes
export interface BaseConfiguredValue {
readRunsPaired: EnaSequencingReads[] | null;
readRunsSingle: EnaSequencingReads[] | null;
tracks: UcscTrack[] | null;
}

// ASSEMBLY scope: workflows that operate on a specific genome assembly
export interface AssemblyConfiguredValue extends BaseConfiguredValue {
_scope: "ASSEMBLY";
designFormula: string | null;
geneModelUrl: string | null;
numberOfHits?: number;
primaryContrasts: PrimaryContrasts | null;
readRunsPaired: EnaSequencingReads[] | null;
readRunsSingle: EnaSequencingReads[] | null;
referenceAssembly: string;
sampleSheet: Record<string, string>[] | null;
sampleSheetClassification: Record<string, COLUMN_TYPE | null> | null;
sequence?: string;
strandedness: Strandedness | undefined;
tracks: UcscTrack[] | null;
}

// ORGANISM scope: workflows that operate at organism level (may not require specific assembly)
// Organism scope workflows may have collection_spec (defined in workflow YAML) and variables.
// fastaCollection can be either:
// - User-selected assembly accessions (string[]) for user-defined FASTA collections
// - Auto-configured via collection_spec in workflow YAML (handled by Galaxy API)
export interface OrganismConfiguredValue extends BaseConfiguredValue {
_scope: "ORGANISM";
fastaCollection: string[] | null;
}

// SEQUENCE scope: workflows that operate on user-provided sequences
export interface SequenceConfiguredValue extends BaseConfiguredValue {
_scope: "SEQUENCE";
numberOfHits?: number;
sequence?: string;
}

// Type guards for ConfiguredValue discrimination
export function isAssemblyConfiguredValue(
value: ConfiguredValue
): value is AssemblyConfiguredValue {
return value._scope === "ASSEMBLY";
}

export function isOrganismConfiguredValue(
value: ConfiguredValue
): value is OrganismConfiguredValue {
return value._scope === "ORGANISM";
}

export function isSequenceConfiguredValue(
value: ConfiguredValue
): value is SequenceConfiguredValue {
return value._scope === "SEQUENCE";
}

// Union type for all configured values
export type ConfiguredValue =
| AssemblyConfiguredValue
| OrganismConfiguredValue
| SequenceConfiguredValue;

export interface Status {
disabled: boolean;
error: string | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,115 @@ import { CUSTOM_WORKFLOW } from "../../../../../../../../../../../../views/Analy
import { DIFFERENTIAL_EXPRESSION_ANALYSIS } from "../../../../../../../../../../../../views/AnalyzeWorkflowsView/differentialExpressionAnalysis/constants";
import { LEXICMAP } from "../../../../../../../../../../../../views/AnalyzeWorkflowsView/lexicmap/constants";
import { LOGAN_SEARCH } from "../../../../../../../../../../../../views/AnalyzeWorkflowsView/loganSearch/constants";
import { Props, UseLaunchGalaxy } from "./types";
import { Workflow } from "../../../../../../../../../../../../apis/catalog/brc-analytics-catalog/common/entities";
import {
ConfiguredValue,
isAssemblyConfiguredValue,
isSequenceConfiguredValue,
Props,
UseLaunchGalaxy,
} from "./types";
import { getConfiguredValues, launchGalaxy } from "./utils";

/**
* Gets the appropriate landing URL based on workflow type.
* @param workflow - Workflow to launch.
* @param configuredValue - Configured values for the workflow.
* @param origin - Origin URL for the Galaxy instance.
* @param run - Async runner function from useAsync hook.
* @returns Promise resolving to the Galaxy workflow landing URL.
*/
async function getLandingUrlForWorkflow(
workflow: Workflow,
configuredValue: ConfiguredValue,
origin: string,
run: (promise: Promise<string>) => Promise<string>
): Promise<string> {
if (workflow.trsId === CUSTOM_WORKFLOW.trsId) {
if (!isAssemblyConfiguredValue(configuredValue)) {
throw new Error("Invalid configured value for CUSTOM workflow");
}
return run(
getDataLandingUrl(
configuredValue.referenceAssembly,
configuredValue.geneModelUrl,
configuredValue.readRunsSingle,
configuredValue.readRunsPaired,
null,
configuredValue.tracks,
origin
)
);
}

if (
workflow.trsId === LOGAN_SEARCH.trsId ||
workflow.trsId === LEXICMAP.trsId
) {
if (!workflow.workflowId) {
throw new Error("Missing workflow ID for LMLS workflow");
}
if (
!isSequenceConfiguredValue(configuredValue) ||
!configuredValue.numberOfHits ||
!configuredValue.sequence
) {
throw new Error("Missing required values for LMLS workflow");
}
return run(
getLMLSLandingUrl(
workflow.workflowId,
configuredValue.numberOfHits,
configuredValue.sequence,
origin
)
);
}

if (workflow.trsId === DIFFERENTIAL_EXPRESSION_ANALYSIS.trsId) {
if (
!workflow.workflowId ||
!isAssemblyConfiguredValue(configuredValue) ||
!configuredValue.geneModelUrl ||
!configuredValue.sampleSheet ||
!configuredValue.sampleSheetClassification ||
!configuredValue.designFormula
) {
throw new Error("Missing required values for DE workflow");
}
return run(
getDeSeq2LandingUrl(
workflow.workflowId,
configuredValue.referenceAssembly,
configuredValue.geneModelUrl,
configuredValue.sampleSheet,
configuredValue.sampleSheetClassification,
configuredValue.designFormula,
configuredValue.primaryContrasts,
configuredValue.strandedness,
origin
)
);
}

return run(
getWorkflowLandingUrl(
workflow.trsId,
isAssemblyConfiguredValue(configuredValue)
? configuredValue.referenceAssembly
: "",
isAssemblyConfiguredValue(configuredValue)
? configuredValue.geneModelUrl
: null,
configuredValue.readRunsSingle,
configuredValue.readRunsPaired,
null,
workflow.parameters,
origin
)
);
}

export const useLaunchGalaxy = ({
configuredInput,
workflow,
Expand All @@ -27,79 +133,17 @@ export const useLaunchGalaxy = ({
if (!configuredValue) return;
const origin = config.browserURL || window.location.origin;

let landingUrl = "";

if (workflow.trsId === CUSTOM_WORKFLOW.trsId) {
landingUrl = await run(
getDataLandingUrl(
configuredValue.referenceAssembly,
configuredValue.geneModelUrl,
configuredValue.readRunsSingle,
configuredValue.readRunsPaired,
null, // fastaCollection - not yet supported in UI
configuredValue.tracks,
origin
)
);
} else if (
workflow.trsId === LOGAN_SEARCH.trsId ||
workflow.trsId === LEXICMAP.trsId
) {
// LMLS workflows use stored workflow IDs with sequence and numberOfHits parameters
if (!workflow.workflowId) {
throw new Error("Missing workflow ID for LMLS workflow");
}
landingUrl = await run(
getLMLSLandingUrl(
workflow.workflowId,
configuredValue.numberOfHits!,
configuredValue.sequence!,
origin
)
);
} else if (workflow.trsId === DIFFERENTIAL_EXPRESSION_ANALYSIS.trsId) {
if (
!workflow.workflowId ||
!configuredValue.geneModelUrl ||
!configuredValue.sampleSheet ||
!configuredValue.sampleSheetClassification ||
!configuredValue.designFormula
) {
throw new Error("Missing required values for DE workflow");
}
landingUrl = await run(
getDeSeq2LandingUrl(
workflow.workflowId,
configuredValue.referenceAssembly,
configuredValue.geneModelUrl,
configuredValue.sampleSheet,
configuredValue.sampleSheetClassification,
configuredValue.designFormula,
configuredValue.primaryContrasts,
configuredValue.strandedness,
origin
)
);
} else {
landingUrl = await run(
getWorkflowLandingUrl(
workflow.trsId,
configuredValue.referenceAssembly,
configuredValue.geneModelUrl,
configuredValue.readRunsSingle,
configuredValue.readRunsPaired,
null, // fastaCollection - not yet supported in UI
workflow.parameters,
origin
)
);
}
const landingUrl = await getLandingUrlForWorkflow(
workflow,
configuredValue,
origin,
run
);

if (!landingUrl) {
throw new Error("Failed to retrieve Galaxy workflow launch URL.");
}

// Launch the Galaxy workflow.
launchGalaxy(landingUrl);
}, [config, configuredValue, run, workflow]);

Expand Down
Loading
Loading