diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/LogicalOp.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/LogicalOp.scala
index 931596b1bf8..3a0f0a5c4b1 100644
--- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/LogicalOp.scala
+++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/LogicalOp.scala
@@ -75,6 +75,7 @@ import org.apache.texera.amber.operator.source.apis.twitter.v2.{
TwitterFullArchiveSearchSourceOpDesc,
TwitterSearchSourceOpDesc
}
+import org.apache.texera.amber.operator.source.dataset.FileListerSourceOpDesc
import org.apache.texera.amber.operator.source.fetcher.URLFetcherOpDesc
import org.apache.texera.amber.operator.source.scan.FileScanSourceOpDesc
import org.apache.texera.amber.operator.source.scan.arrow.ArrowSourceOpDesc
@@ -158,6 +159,7 @@ trait StateTransferFunc
new Type(value = classOf[IfOpDesc], name = "If"),
new Type(value = classOf[SankeyDiagramOpDesc], name = "SankeyDiagram"),
new Type(value = classOf[IcicleChartOpDesc], name = "IcicleChart"),
+ new Type(value = classOf[FileListerSourceOpDesc], name = "FileLister"),
new Type(value = classOf[CSVScanSourceOpDesc], name = "CSVFileScan"),
// disabled the ParallelCSVScanSourceOpDesc so that it does not confuse user. it can be re-enabled when doing experiments.
// new Type(value = classOf[ParallelCSVScanSourceOpDesc], name = "ParallelCSVFileScan"),
diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/source/dataset/FileListerSourceOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/source/dataset/FileListerSourceOpDesc.scala
new file mode 100644
index 00000000000..1101dce7e00
--- /dev/null
+++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/source/dataset/FileListerSourceOpDesc.scala
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.amber.operator.source.dataset
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle
+import org.apache.texera.amber.core.executor.OpExecWithClassName
+import org.apache.texera.amber.core.tuple.{AttributeType, Schema}
+import org.apache.texera.amber.core.virtualidentity.{ExecutionIdentity, WorkflowIdentity}
+import org.apache.texera.amber.core.workflow.{OutputPort, PhysicalOp, SchemaPropagationFunc}
+import org.apache.texera.amber.operator.LogicalOp
+import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo}
+import org.apache.texera.amber.util.JSONUtils.objectMapper
+
+class FileListerSourceOpDesc extends LogicalOp {
+
+ @JsonProperty(required = true)
+ @JsonSchemaTitle("Dataset")
+ var datasetVersionPath: String = _
+
+ override def getPhysicalOp(
+ workflowId: WorkflowIdentity,
+ executionId: ExecutionIdentity
+ ): PhysicalOp =
+ PhysicalOp
+ .sourcePhysicalOp(
+ workflowId,
+ executionId,
+ operatorIdentifier,
+ OpExecWithClassName(
+ "org.apache.texera.amber.operator.source.dataset.FileListerSourceOpExec",
+ objectMapper.writeValueAsString(this)
+ )
+ )
+ .withInputPorts(operatorInfo.inputPorts)
+ .withOutputPorts(operatorInfo.outputPorts)
+ .withPropagateSchema(
+ SchemaPropagationFunc(_ =>
+ Map(operatorInfo.outputPorts.head.id -> Schema().add("filename", AttributeType.STRING))
+ )
+ )
+
+ override def operatorInfo: OperatorInfo =
+ OperatorInfo(
+ userFriendlyName = "File Lister",
+ operatorDescription = "Select a dataset version and output one filename tuple per file",
+ operatorGroupName = OperatorGroupConstants.INPUT_GROUP,
+ inputPorts = List.empty,
+ outputPorts = List(OutputPort())
+ )
+}
diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/source/dataset/FileListerSourceOpExec.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/source/dataset/FileListerSourceOpExec.scala
new file mode 100644
index 00000000000..f715a0943fa
--- /dev/null
+++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/source/dataset/FileListerSourceOpExec.scala
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.amber.operator.source.dataset
+
+import org.apache.texera.amber.core.executor.SourceOperatorExecutor
+import org.apache.texera.amber.core.storage.util.LakeFSStorageClient
+import org.apache.texera.amber.core.tuple.TupleLike
+import org.apache.texera.amber.util.JSONUtils.objectMapper
+import org.apache.texera.dao.SqlServer
+import org.apache.texera.dao.jooq.generated.tables.Dataset.DATASET
+import org.apache.texera.dao.jooq.generated.tables.DatasetVersion.DATASET_VERSION
+import org.apache.texera.dao.jooq.generated.tables.User.USER
+
+class FileListerSourceOpExec private[dataset] (descString: String)
+ extends SourceOperatorExecutor {
+ private val desc: FileListerSourceOpDesc =
+ objectMapper.readValue(descString, classOf[FileListerSourceOpDesc])
+
+ override def produceTuple(): Iterator[TupleLike] = {
+ val Seq(_, ownerEmail, datasetName, versionName) =
+ desc.datasetVersionPath.split("/").toSeq
+
+ val (repositoryName, versionHash) =
+ SqlServer
+ .getInstance()
+ .createDSLContext()
+ .select(DATASET.REPOSITORY_NAME, DATASET_VERSION.VERSION_HASH)
+ .from(DATASET)
+ .join(USER)
+ .on(USER.UID.eq(DATASET.OWNER_UID))
+ .join(DATASET_VERSION)
+ .on(DATASET_VERSION.DID.eq(DATASET.DID))
+ .where(USER.EMAIL.eq(ownerEmail))
+ .and(DATASET.NAME.eq(datasetName))
+ .and(DATASET_VERSION.NAME.eq(versionName))
+ .fetchOne(r => (r.value1(), r.value2()))
+
+ LakeFSStorageClient
+ .retrieveObjectsOfVersion(repositoryName, versionHash)
+ .map(obj => TupleLike("filename" -> s"${desc.datasetVersionPath}/${obj.getPath}"))
+ .iterator
+ }
+}
diff --git a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/source/dataset/FileListerSourceOpDescSpec.scala b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/source/dataset/FileListerSourceOpDescSpec.scala
new file mode 100644
index 00000000000..a5aa744ff5a
--- /dev/null
+++ b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/source/dataset/FileListerSourceOpDescSpec.scala
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.amber.operator.source.dataset
+
+import org.apache.texera.amber.core.tuple.AttributeType
+import org.scalatest.flatspec.AnyFlatSpec
+
+class FileListerSourceOpDescSpec extends AnyFlatSpec {
+
+ "FileListerSourceOpDesc" should "expose a filename output column" in {
+ val opDesc = new FileListerSourceOpDesc()
+
+ val outputSchema = opDesc.getExternalOutputSchemas(Map.empty).values.head
+
+ assert(outputSchema.getAttributes.length == 1)
+ assert(outputSchema.getAttribute("filename").getType == AttributeType.STRING)
+ }
+
+ it should "use the expected operator metadata" in {
+ val opDesc = new FileListerSourceOpDesc()
+
+ assert(opDesc.operatorInfo.userFriendlyName == "File Lister")
+ assert(opDesc.operatorInfo.inputPorts.isEmpty)
+ assert(opDesc.operatorInfo.outputPorts.length == 1)
+ }
+}
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index b6364e781f9..11ce2f26f89 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -103,7 +103,9 @@ import { CoeditorUserIconComponent } from "./workspace/component/menu/coeditor-u
import { AgentPanelComponent } from "./workspace/component/agent-panel/agent-panel.component";
import { AgentChatComponent } from "./workspace/component/agent-panel/agent-chat/agent-chat.component";
import { AgentRegistrationComponent } from "./workspace/component/agent-panel/agent-registration/agent-registration.component";
-import { InputAutoCompleteComponent } from "./workspace/component/input-autocomplete/input-autocomplete.component";
+import { DatasetFileSelectorComponent } from "./workspace/component/dataset-file-selector/dataset-file-selector.component";
+import { DatasetVersionSelectorComponent } from "./workspace/component/dataset-version-selector/dataset-version-selector.component";
+import { DatasetSelectionModalComponent } from "./workspace/component/dataset-selection-modal/dataset-selection-modal.component";
import { CollabWrapperComponent } from "./common/formly/collab-wrapper/collab-wrapper/collab-wrapper.component";
import { TexeraCopilot } from "./workspace/service/copilot/texera-copilot";
import { NzSwitchModule } from "ng-zorro-antd/switch";
@@ -152,7 +154,6 @@ import { NzTreeModule } from "ng-zorro-antd/tree";
import { NzTreeViewModule } from "ng-zorro-antd/tree-view";
import { NzNoAnimationModule } from "ng-zorro-antd/core/no-animation";
import { TreeModule } from "@ali-hm/angular-tree-component";
-import { FileSelectionComponent } from "./workspace/component/file-selection/file-selection.component";
import { ResultExportationComponent } from "./workspace/component/result-exportation/result-exportation.component";
import { ReportGenerationService } from "./workspace/service/report-generation/report-generation.service";
import { SearchBarComponent } from "./dashboard/component/user/search-bar/search-bar.component";
@@ -257,8 +258,9 @@ registerLocaleData(en);
AgentPanelComponent,
AgentChatComponent,
AgentRegistrationComponent,
- InputAutoCompleteComponent,
- FileSelectionComponent,
+ DatasetFileSelectorComponent,
+ DatasetVersionSelectorComponent,
+ DatasetSelectionModalComponent,
CollabWrapperComponent,
AboutComponent,
UserWorkflowListItemComponent,
diff --git a/frontend/src/app/common/formly/formly-config.ts b/frontend/src/app/common/formly/formly-config.ts
index d950bd3690c..c3995abb544 100644
--- a/frontend/src/app/common/formly/formly-config.ts
+++ b/frontend/src/app/common/formly/formly-config.ts
@@ -24,9 +24,10 @@ import { MultiSchemaTypeComponent } from "./multischema.type";
import { FormlyFieldConfig } from "@ngx-formly/core";
import { CodeareaCustomTemplateComponent } from "../../workspace/component/codearea-custom-template/codearea-custom-template.component";
import { PresetWrapperComponent } from "./preset-wrapper/preset-wrapper.component";
-import { InputAutoCompleteComponent } from "../../workspace/component/input-autocomplete/input-autocomplete.component";
+import { DatasetFileSelectorComponent } from "../../workspace/component/dataset-file-selector/dataset-file-selector.component";
import { CollabWrapperComponent } from "./collab-wrapper/collab-wrapper/collab-wrapper.component";
import { FormlyRepeatDndComponent } from "./repeat-dnd/repeat-dnd.component";
+import { DatasetVersionSelectorComponent } from "../../workspace/component/dataset-version-selector/dataset-version-selector.component";
/**
* Configuration for using Json Schema with Formly.
@@ -76,7 +77,8 @@ export const TEXERA_FORMLY_CONFIG = {
{ name: "object", component: ObjectTypeComponent },
{ name: "multischema", component: MultiSchemaTypeComponent },
{ name: "codearea", component: CodeareaCustomTemplateComponent },
- { name: "inputautocomplete", component: InputAutoCompleteComponent, wrappers: ["form-field"] },
+ { name: "inputautocomplete", component: DatasetFileSelectorComponent, wrappers: ["form-field"] },
+ { name: "datasetversionselector", component: DatasetVersionSelectorComponent, wrappers: ["form-field"] },
{ name: "repeat-section-dnd", component: FormlyRepeatDndComponent },
],
wrappers: [
diff --git a/frontend/src/app/workspace/component/input-autocomplete/input-autocomplete.component.html b/frontend/src/app/workspace/component/dataset-file-selector/dataset-file-selector.component.html
similarity index 52%
rename from frontend/src/app/workspace/component/input-autocomplete/input-autocomplete.component.html
rename to frontend/src/app/workspace/component/dataset-file-selector/dataset-file-selector.component.html
index 53ee531fa6e..b4f0b58d5e4 100644
--- a/frontend/src/app/workspace/component/input-autocomplete/input-autocomplete.component.html
+++ b/frontend/src/app/workspace/component/dataset-file-selector/dataset-file-selector.component.html
@@ -16,27 +16,16 @@
specific language governing permissions and limitations
under the License.
-->
-
-
-
-
-
-
-
-
+
+
diff --git a/frontend/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts b/frontend/src/app/workspace/component/dataset-file-selector/dataset-file-selector.component.ts
similarity index 64%
rename from frontend/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts
rename to frontend/src/app/workspace/component/dataset-file-selector/dataset-file-selector.component.ts
index 38839f377e5..a1fba605b3e 100644
--- a/frontend/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts
+++ b/frontend/src/app/workspace/component/dataset-file-selector/dataset-file-selector.component.ts
@@ -22,17 +22,14 @@ import { FieldType, FieldTypeConfig } from "@ngx-formly/core";
import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy";
import { WorkflowActionService } from "../../service/workflow-graph/model/workflow-action.service";
import { NzModalService } from "ng-zorro-antd/modal";
-import { FileSelectionComponent } from "../file-selection/file-selection.component";
-import { DatasetFileNode, getFullPathFromDatasetFileNode } from "../../../common/type/datasetVersionFileTree";
+import { DatasetSelectionModalComponent } from "../dataset-selection-modal/dataset-selection-modal.component";
import { GuiConfigService } from "../../../common/service/gui-config.service";
@UntilDestroy()
@Component({
- selector: "texera-input-autocomplete-template",
- templateUrl: "./input-autocomplete.component.html",
- styleUrls: ["input-autocomplete.component.scss"],
+ templateUrl: "dataset-file-selector.component.html",
})
-export class InputAutoCompleteComponent extends FieldType {
+export class DatasetFileSelectorComponent extends FieldType {
constructor(
private modalService: NzModalService,
public workflowActionService: WorkflowActionService,
@@ -43,14 +40,13 @@ export class InputAutoCompleteComponent extends FieldType {
onClickOpenFileSelectionModal(): void {
const modal = this.modalService.create({
- nzTitle: "Please select one file from datasets",
- nzContent: FileSelectionComponent,
+ nzContent: DatasetSelectionModalComponent,
nzFooter: null,
nzData: {
- selectedFilePath: this.formControl.getRawValue(),
+ selectFile: true,
+ selectedPath: this.formControl.getRawValue(),
},
nzBodyStyle: {
- // Enables the file selection window to be resizable
resize: "both",
overflow: "auto",
minHeight: "200px",
@@ -60,22 +56,14 @@ export class InputAutoCompleteComponent extends FieldType {
},
nzWidth: "fit-content",
});
- // Handle the selection from the modal
- modal.afterClose.pipe(untilDestroyed(this)).subscribe(fileNode => {
- const node: DatasetFileNode = fileNode as DatasetFileNode;
- this.formControl.setValue(getFullPathFromDatasetFileNode(node));
+ modal.afterClose.pipe(untilDestroyed(this)).subscribe(selectedPath => {
+ if (selectedPath) {
+ this.formControl.setValue(selectedPath);
+ }
});
}
- get enableDatasetSource(): boolean {
- return this.config.env.selectingFilesFromDatasetsEnabled;
- }
-
get isFileSelectionEnabled(): boolean {
- return this.enableDatasetSource;
- }
-
- get selectedFilePath(): string | null {
- return this.formControl.value;
+ return this.config.env.selectingFilesFromDatasetsEnabled;
}
}
diff --git a/frontend/src/app/workspace/component/dataset-selection-modal/dataset-selection-modal.component.html b/frontend/src/app/workspace/component/dataset-selection-modal/dataset-selection-modal.component.html
new file mode 100644
index 00000000000..11cd7b1c3de
--- /dev/null
+++ b/frontend/src/app/workspace/component/dataset-selection-modal/dataset-selection-modal.component.html
@@ -0,0 +1,69 @@
+
+
+
+
-
diff --git a/frontend/src/app/workspace/component/file-selection/file-selection.component.scss b/frontend/src/app/workspace/component/file-selection/file-selection.component.scss
deleted file mode 100644
index 0e7ceadbf9b..00000000000
--- a/frontend/src/app/workspace/component/file-selection/file-selection.component.scss
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-:host {
- display: block;
- padding: 16px;
-}
-
-.container {
- display: flex;
- flex-direction: column;
- gap: 16px;
-}
-
-.selection-row {
- display: flex;
- gap: 16px;
-}
-
-.nz-select {
- width: 100%;
-}
-
-.select-dataset {
- transition: width 0.3s; /* Add animation effect */
-}
-
-.select-version {
- flex: 1;
- min-width: 0;
-}
-
-.texera-user-dataset-version-filetree {
- margin-top: 16px;
-}
-
-.dataset-option {
- display: flex;
- align-items: center;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.dataset-name {
- display: inline-block;
- max-width: calc(100% - 100px); /* Adjust to fit other elements */
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.dataset-id-container {
- background-color: grey;
- color: white;
- width: 23px;
- height: 23px;
- border-radius: 50%;
- display: flex;
- justify-content: center;
- align-items: center;
- font-size: 12px;
- margin-right: 5px;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
- overflow: hidden;
-}
-
-.dataset-owner,
-.dataset-access-privilege {
- margin-left: 10px;
- font-size: 12px;
- color: grey;
-}
diff --git a/frontend/src/app/workspace/component/file-selection/file-selection.component.ts b/frontend/src/app/workspace/component/file-selection/file-selection.component.ts
deleted file mode 100644
index dfae2e48350..00000000000
--- a/frontend/src/app/workspace/component/file-selection/file-selection.component.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { Component, inject, OnInit } from "@angular/core";
-import { NZ_MODAL_DATA, NzModalRef } from "ng-zorro-antd/modal";
-import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy";
-import { DatasetFileNode } from "../../../common/type/datasetVersionFileTree";
-import { DatasetVersion } from "../../../common/type/dataset";
-import { DashboardDataset } from "../../../dashboard/type/dashboard-dataset.interface";
-import { DatasetService } from "../../../dashboard/service/user/dataset/dataset.service";
-import { parseFilePathToDatasetFile } from "../../../common/type/dataset-file";
-
-@UntilDestroy()
-@Component({
- selector: "texera-file-selection-model",
- templateUrl: "file-selection.component.html",
- styleUrls: ["file-selection.component.scss"],
-})
-export class FileSelectionComponent implements OnInit {
- readonly selectedFilePath: string = inject(NZ_MODAL_DATA).selectedFilePath;
- private _datasets: ReadonlyArray = [];
-
- // indicate whether the accessible datasets have been loaded from the backend
- isAccessibleDatasetsLoading = true;
-
- selectedDataset?: DashboardDataset;
- selectedVersion?: DatasetVersion;
- datasetVersions?: DatasetVersion[];
- suggestedFileTreeNodes: DatasetFileNode[] = [];
- isDatasetSelected: boolean = false;
-
- constructor(
- private modalRef: NzModalRef,
- private datasetService: DatasetService
- ) {}
-
- ngOnInit() {
- this.isAccessibleDatasetsLoading = true;
-
- // retrieve all the accessible datasets from the backend
- this.datasetService
- .retrieveAccessibleDatasets()
- .pipe(untilDestroyed(this))
- .subscribe(datasets => {
- this._datasets = datasets;
- this.isAccessibleDatasetsLoading = false;
- if (!this.selectedFilePath || this.selectedFilePath == "") {
- return;
- }
- // if users already select some file, then ONLY show that selected dataset & related version
- const selectedDatasetFile = parseFilePathToDatasetFile(this.selectedFilePath);
- this.selectedDataset = this.datasets.find(
- d => d.ownerEmail === selectedDatasetFile.ownerEmail && d.dataset.name === selectedDatasetFile.datasetName
- );
- this.isDatasetSelected = !!this.selectedDataset;
- if (this.selectedDataset && this.selectedDataset.dataset.did !== undefined) {
- this.datasetService
- .retrieveDatasetVersionList(this.selectedDataset.dataset.did)
- .pipe(untilDestroyed(this))
- .subscribe(versions => {
- this.datasetVersions = versions;
- this.selectedVersion = this.datasetVersions.find(v => v.name === selectedDatasetFile.versionName);
- this.onVersionChange();
- });
- }
- });
- }
-
- onDatasetChange() {
- this.selectedVersion = undefined;
- this.suggestedFileTreeNodes = [];
- this.isDatasetSelected = !!this.selectedDataset;
- if (this.selectedDataset && this.selectedDataset.dataset.did !== undefined) {
- this.datasetService
- .retrieveDatasetVersionList(this.selectedDataset.dataset.did)
- .pipe(untilDestroyed(this))
- .subscribe(versions => {
- this.datasetVersions = versions;
- if (this.datasetVersions && this.datasetVersions.length > 0) {
- this.selectedVersion = this.datasetVersions[0];
- this.onVersionChange();
- }
- });
- }
- }
-
- onVersionChange() {
- this.suggestedFileTreeNodes = [];
- if (
- this.selectedDataset &&
- this.selectedDataset.dataset.did !== undefined &&
- this.selectedVersion &&
- this.selectedVersion.dvid !== undefined
- ) {
- this.datasetService
- .retrieveDatasetVersionFileTree(this.selectedDataset.dataset.did, this.selectedVersion.dvid)
- .pipe(untilDestroyed(this))
- .subscribe(data => {
- this.suggestedFileTreeNodes = data.fileNodes;
- });
- }
- }
-
- onFileTreeNodeSelected(node: DatasetFileNode) {
- this.modalRef.close(node);
- }
-
- get datasets(): ReadonlyArray {
- return this._datasets;
- }
-}
diff --git a/frontend/src/app/workspace/component/input-autocomplete/input-autocomplete.component.spec.ts b/frontend/src/app/workspace/component/input-autocomplete/input-autocomplete.component.spec.ts
deleted file mode 100644
index 690ad849990..00000000000
--- a/frontend/src/app/workspace/component/input-autocomplete/input-autocomplete.component.spec.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing";
-import { FormControl, ReactiveFormsModule } from "@angular/forms";
-import { InputAutoCompleteComponent } from "./input-autocomplete.component";
-import { HttpClientTestingModule } from "@angular/common/http/testing";
-import { NzModalService } from "ng-zorro-antd/modal";
-import { commonTestProviders } from "../../../common/testing/test-utils";
-
-describe("InputAutoCompleteComponent", () => {
- let component: InputAutoCompleteComponent;
- let fixture: ComponentFixture;
-
- beforeEach(waitForAsync(() => {
- TestBed.configureTestingModule({
- declarations: [InputAutoCompleteComponent],
- imports: [ReactiveFormsModule, HttpClientTestingModule],
- providers: [NzModalService, ...commonTestProviders],
- }).compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(InputAutoCompleteComponent);
- component = fixture.componentInstance;
- component.field = { props: {}, formControl: new FormControl() };
- fixture.detectChanges();
- });
-
- it("should create", () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts b/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts
index 5d457e9050e..0e1484f59c1 100644
--- a/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts
+++ b/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts
@@ -449,10 +449,14 @@ export class OperatorPropertyEditFrameComponent implements OnInit, OnChanges, On
}
// if the title is fileName, then change it to custom autocomplete input template
- if (mappedField.key == "fileName") {
+ if (mappedField.key === "fileName") {
mappedField.type = "inputautocomplete";
}
+ if (mappedField.key === "datasetVersionPath") {
+ mappedField.type = "datasetversionselector";
+ }
+
// if the title is python script (for Python UDF), then make this field a custom template 'codearea'
if (mapSource?.description?.toLowerCase() === "input your code here") {
if (mappedField.type) {
diff --git a/frontend/src/assets/operator_images/FileLister.png b/frontend/src/assets/operator_images/FileLister.png
new file mode 100644
index 00000000000..c570e230ac2
Binary files /dev/null and b/frontend/src/assets/operator_images/FileLister.png differ