From 57364d6c53b6a25a6f15e0fe96eca042cda23d08 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Sun, 3 Mar 2024 22:56:19 -0800 Subject: [PATCH 01/10] move changes --- core/new-gui/src/app/app.module.ts | 2 + .../workflow-persist.service.ts | 8 + .../src/app/common/type/environment.ts | 20 ++ .../user-dataset-file-renderer.component.ts | 3 - .../service/user-dataset/dataset.service.ts | 4 + .../user-environment/environment.service.ts | 73 +++++ .../environment/environment.component.html | 144 ++++++++++ .../environment/environment.component.scss | 99 +++++++ .../environment/environment.component.ts | 253 ++++++++++++++++++ .../left-panel/left-panel.component.ts | 9 +- 10 files changed, 611 insertions(+), 4 deletions(-) create mode 100644 core/new-gui/src/app/common/type/environment.ts create mode 100644 core/new-gui/src/app/dashboard/user/service/user-environment/environment.service.ts create mode 100644 core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.html create mode 100644 core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.scss create mode 100644 core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts diff --git a/core/new-gui/src/app/app.module.ts b/core/new-gui/src/app/app.module.ts index d8cf0f74640..cdccb977abd 100644 --- a/core/new-gui/src/app/app.module.ts +++ b/core/new-gui/src/app/app.module.ts @@ -134,6 +134,7 @@ import { NgxFileDropModule } from "ngx-file-drop"; 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 {EnvironmentComponent} from "./workspace/component/left-panel/environment/environment.component"; registerLocaleData(en); @@ -148,6 +149,7 @@ registerLocaleData(en); PropertyEditorComponent, VersionsListComponent, TimeTravelComponent, + EnvironmentComponent, WorkflowEditorComponent, ResultPanelComponent, OperatorLabelComponent, diff --git a/core/new-gui/src/app/common/service/workflow-persist/workflow-persist.service.ts b/core/new-gui/src/app/common/service/workflow-persist/workflow-persist.service.ts index 9c60b855000..53234e2bf30 100644 --- a/core/new-gui/src/app/common/service/workflow-persist/workflow-persist.service.ts +++ b/core/new-gui/src/app/common/service/workflow-persist/workflow-persist.service.ts @@ -8,6 +8,7 @@ import { DashboardWorkflow } from "../../../dashboard/user/type/dashboard-workfl import { WorkflowUtilService } from "../../../workspace/service/workflow-graph/util/workflow-util.service"; import { NotificationService } from "../notification/notification.service"; import { SearchFilterParameters, toQueryStrings } from "src/app/dashboard/user/type/search-filter-parameters"; +import {Environment} from "../../type/environment"; export const WORKFLOW_BASE_URL = "workflow"; export const WORKFLOW_PERSIST_URL = WORKFLOW_BASE_URL + "/persist"; @@ -20,6 +21,7 @@ export const WORKFLOW_UPDATENAME_URL = WORKFLOW_BASE_URL + "/update/name"; export const WORKFLOW_UPDATEDESCRIPTION_URL = WORKFLOW_BASE_URL + "/update/description"; export const WORKFLOW_OWNER_URL = WORKFLOW_BASE_URL + "/user-workflow-owners"; export const WORKFLOW_ID_URL = WORKFLOW_BASE_URL + "/user-workflow-ids"; +export const WORKFLOW_ENVIRONMENT = "/environment"; export const DEFAULT_WORKFLOW_NAME = "Untitled workflow"; @@ -190,4 +192,10 @@ export class WorkflowPersistService { public retrieveWorkflowIDs(): Observable { return this.http.get(`${AppSettings.getApiEndpoint()}/${WORKFLOW_ID_URL}`); } + + public retrieveWorkflowEnvironment(wid: number): Observable { + return this.http + .get(`${AppSettings.getApiEndpoint()}/${WORKFLOW_BASE_URL}/${wid}/${WORKFLOW_ENVIRONMENT}`) + .pipe(); + } } diff --git a/core/new-gui/src/app/common/type/environment.ts b/core/new-gui/src/app/common/type/environment.ts new file mode 100644 index 00000000000..9228f86ecf5 --- /dev/null +++ b/core/new-gui/src/app/common/type/environment.ts @@ -0,0 +1,20 @@ +import {Dataset, DatasetVersion} from "./dataset"; + +export interface Environment { + eid: number | undefined; + uid: number | undefined; + name: string; + description: string; + creationTime: number | undefined; +} + +export interface DatasetOfEnvironment { + did: number; + eid: number; + dvid: number; +} + +export interface DatasetOfEnvironmentDetails { + dataset: Dataset; + version: DatasetVersion; +} diff --git a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-file-renderer/user-dataset-file-renderer.component.ts b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-file-renderer/user-dataset-file-renderer.component.ts index 15236f35f0a..8c5f9b9e103 100644 --- a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-file-renderer/user-dataset-file-renderer.component.ts +++ b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-file-renderer/user-dataset-file-renderer.component.ts @@ -159,14 +159,11 @@ export class UserDatasetFileRendererComponent implements OnInit, OnChanges, OnDe case MIME_TYPES.MP4: this.displayMP4 = true; this.loadSafeURL(blob); - this.notificationService.info("Video display might not be supported by some browsers."); break; case MIME_TYPES.MP3: this.displayMP3 = true; this.loadSafeURL(blob); - this.notificationService.info("Audio display might not be supported by some browsers."); - break; case MIME_TYPES.MSEXCEL: diff --git a/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts b/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts index ae19c72457c..d215fb8f40c 100644 --- a/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts +++ b/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts @@ -73,6 +73,10 @@ export class DatasetService { ); } + public retrieveAccessibleDatasets(): Observable { + return this.http.get(`${AppSettings.getApiEndpoint()}/${DATASET_BASE_URL}`); + } + public createDatasetVersion( did: number, newVersion: string, diff --git a/core/new-gui/src/app/dashboard/user/service/user-environment/environment.service.ts b/core/new-gui/src/app/dashboard/user/service/user-environment/environment.service.ts new file mode 100644 index 00000000000..09231a595be --- /dev/null +++ b/core/new-gui/src/app/dashboard/user/service/user-environment/environment.service.ts @@ -0,0 +1,73 @@ +import { Injectable } from "@angular/core"; + +import { HttpClient } from "@angular/common/http"; +import next from "ajv/dist/vocabularies/next"; +import { Observable, of, throwError } from "rxjs"; +import { catchError, filter, map } from "rxjs/operators"; +import { AppSettings } from "../../../../common/app-setting"; +import {DatasetOfEnvironment, DatasetOfEnvironmentDetails, Environment} from "../../../../common/type/environment"; +import {DashboardDataset} from "../../type/dashboard-dataset.interface"; +import {DATASET_BASE_URL} from "../user-dataset/dataset.service"; + +export const ENVIRONMENT_BASE_URL = "environment"; +export const ENVIRONMENT_CREATE_URL = ENVIRONMENT_BASE_URL + "/create"; +export const ENVIRONMENT_DELETE_URL = ENVIRONMENT_BASE_URL + "/delete"; +export const ENVIRONMENT_GET_DATASETS_FILELIST = "/files"; +export const ENVIRONMENT_DATASET_RETRIEVAL_URL = "/dataset"; +export const ENVIRONMENT_DATASET_DETAILS_RETRIEVAL_URL = ENVIRONMENT_DATASET_RETRIEVAL_URL + "/details"; + +export const ENVIRONMENT_DATASET_ADD_URL = ENVIRONMENT_DATASET_RETRIEVAL_URL + "/add"; + +export const ENVIRONMENT_DATASET_REMOVE_URL = ENVIRONMENT_DATASET_RETRIEVAL_URL + "/remove"; + +@Injectable({ + providedIn: "root", +}) +export class EnvironmentService { + constructor( + private http: HttpClient, + ) {} + + addDatasetToEnvironment(eid: number, did: number): Observable { + return this.http.post( + `${AppSettings.getApiEndpoint()}/${ENVIRONMENT_BASE_URL}/${eid}/${ENVIRONMENT_DATASET_ADD_URL}`, + { + did: did, + } + ); + } + + removeDatasetFromEnvironment(eid: number, did: number): Observable { + return this.http.post( + `${AppSettings.getApiEndpoint()}/${ENVIRONMENT_BASE_URL}/${eid}/${ENVIRONMENT_DATASET_REMOVE_URL}`, + { + did: did, + } + ); + } + + retrieveDatasetsOfEnvironment(eid: number): Observable { + return this.http.get( + `${AppSettings.getApiEndpoint()}/${ENVIRONMENT_BASE_URL}/${eid}/${ENVIRONMENT_DATASET_RETRIEVAL_URL}` + ); + } + + retrieveDatasetsOfEnvironmentDetails(eid: number): Observable { + return this.http.get( + `${AppSettings.getApiEndpoint()}/${ENVIRONMENT_BASE_URL}/${eid}/${ENVIRONMENT_DATASET_DETAILS_RETRIEVAL_URL}` + ); + } + + // Delete: Remove an environment by its index (eid) + deleteEnvironments(eids: number[]): Observable { + return this.http.post(`${AppSettings.getApiEndpoint()}/${ENVIRONMENT_DELETE_URL}`, { + eids: eids, + }); + } + + public getDatasetsFileList(eid: number, query: String): Observable> { + return this.http.get>( + `${AppSettings.getApiEndpoint()}/${ENVIRONMENT_BASE_URL}/${eid}/${ENVIRONMENT_GET_DATASETS_FILELIST}/${query}` + ); + } +} diff --git a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.html b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.html new file mode 100644 index 00000000000..7b8cc58887e --- /dev/null +++ b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.html @@ -0,0 +1,144 @@ + + +
    +
  • + + Datasets +
  • + +
+ + +
+ + + + + + + +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Dataset ID{{showingDatasetDid}}
Name{{showingDatasetName}}
Description{{showingDatasetDescription}}
Version Name{{showingDatasetVersionName}}
+
+
+ + + + + + + +
+ + +
diff --git a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.scss b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.scss new file mode 100644 index 00000000000..b4004288b17 --- /dev/null +++ b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.scss @@ -0,0 +1,99 @@ +.link-dataset-btn { + margin-top: 10px; + margin-left: 10px; + margin-bottom: 10px; +} + +.environment-layout { + background-color: white; +} + +.environment-info { + background-color: white; + width: 20px; + margin-left: 20px; +} + +.ant-tooltip-inner { + max-width: 500px; +} + +.datasets-container { + background-color: white; + /* Add other styling as needed, such as padding or margins */ +} + +.dataset-id-container { + background-color: grey; + color: white; /* Assuming you want the text to be white */ + width: 25px; /* You can adjust the size as needed */ + height: 25px; /* Make sure height and width are the same to create a circle */ + border-radius: 50%; /* This makes the div circular */ + display: flex; /* Using flexbox to center content */ + justify-content: center; /* Center horizontally */ + align-items: center; /* Center vertically */ + font-size: 14px; /* Adjust the font size as needed */ + margin-left: 5px; /* If you want to center the circle in its container */ + /* Additional styles for better appearance */ + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Optional: Adds subtle shadow */ + overflow: hidden; /* Ensures content doesn't spill outside the circle */ +} + +.auto-option-content { + width: 100%; + height: 50%; + display: flex; + //align-items: center; /* Aligns items vertically in the center */ + justify-content: space-between; +} + +.dataset-name { + //flex-shrink: 0; /* Prevents the name from shrinking */ + //margin-left: 8px; /* Adds some space if needed */ + margin-left: 10px; + flex-grow: 1; /* This will make the name take up the remaining space */ +} + +.dataset-option-link-btn { + margin-right: 5px; + //width: 20%; + //height: 90%; + //text-align: center; +} + +.custom-table { + width: 100%; /* Make the table take the full width of its container */ + border-collapse: collapse; /* Collapse borders */ +} + +.custom-table td { + padding: 8px; /* Add some padding to table cells */ + border-bottom: 1px solid #e8e8e8; /* Add a subtle line between rows */ +} + +.table-label { + font-weight: bold; /* Make label text bold */ + width: 30%; /* Allocate 30% of the width to labels */ + background-color: #f0f0f0; /* A light grey background for the labels */ +} + +.table-value { + width: 70%; /* Allocate 70% of the width to values */ +} + +.file-renderer { + min-width: 300px; /* Adjust minimum width as needed */ + max-width: 600px; /* Adjust maximum width as needed */ + min-height: 200px; /* Adjust minimum height as needed */ + max-height: 400px; /* Adjust maximum height as needed */ + /* Ensure the component content respects these bounds */ + overflow: auto; /* Add scrollbars within the component if content overflows */ +} + +.file-display-modal .ant-modal-body { + min-width: 300px; /* Adjust minimum width as needed */ + max-width: 600px; /* Adjust maximum width as needed */ + min-height: 200px; /* Adjust minimum height as needed */ + max-height: 400px; /* Adjust maximum height as needed */ + overflow: auto; /* Add scrollbars within the modal if content overflows */ +} diff --git a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts new file mode 100644 index 00000000000..1a5e6fbf3d3 --- /dev/null +++ b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts @@ -0,0 +1,253 @@ +import { EnvironmentService } from "../../../../dashboard/user/service/user-environment/environment.service"; +import { NotificationService } from "../../../../common/service/notification/notification.service"; +import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; +import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; +import { WorkflowPersistService } from "../../../../common/service/workflow-persist/workflow-persist.service"; +import { WorkflowActionService } from "../../../service/workflow-graph/model/workflow-action.service"; +import { + DatasetVersionFileTreeNode, + getFullPathFromFileTreeNode, +} from "../../../../common/type/datasetVersionFileTree"; +import { DatasetService } from "../../../../dashboard/user/service/user-dataset/dataset.service"; +import { DashboardDataset } from "../../../../dashboard/user/type/dashboard-dataset.interface"; +import {DatasetOfEnvironmentDetails, Environment} from "../../../../common/type/environment"; + +@UntilDestroy() +@Component({ + selector: "texera-environment", + templateUrl: "environment.component.html", + styleUrls: ["environment.component.scss"], +}) +export class EnvironmentComponent implements OnInit { + // this input is for other components, + // e.g. environment viewer, that already have the eid to use + @Input() + eid: number | undefined; + + wid: number | undefined; + + selectedMenu: "datasets" = "datasets"; + + environment: Environment | undefined; + environmentTooltip: string = + "Environment manages the workflow related information, including the datasets visible to current workflow.\n"; + + // [did] => [DatasetOfEnvironmentDetails, DatasetVersionFileTreeNode[]] + datasetsOfEnvironment: Map = new Map(); + + // [did, datasetName, DatasetVersionFileTreeNode[]] + datasetFileTrees: [number, string, DatasetVersionFileTreeNode[]][] = []; + + // dataset link related control + showDatasetLinkModal: boolean = false; + userAccessibleDatasets: DashboardDataset[] = []; + filteredLinkingDatasets: { did: number | undefined; name: string }[] = []; + inputDatasetName?: string; + + // dataset details related control + showDatasetDetails: boolean = false; + showingDataset: DatasetOfEnvironmentDetails | undefined; + + // dataset file display related control + showDatasetFile: boolean = false; + showingDatasetFile: DatasetVersionFileTreeNode | undefined; + showingDatasetFileDid: number | undefined; + showingDatasetFileDvid: number | undefined; + + constructor( + private environmentService: EnvironmentService, + private notificationService: NotificationService, + private workflowPersistService: WorkflowPersistService, + private workflowActionService: WorkflowActionService, + private datasetService: DatasetService, + ) {} + + ngOnInit(): void { + // initialize the environment info + this.wid = this.workflowActionService.getWorkflowMetadata()?.wid; + if (this.wid) { + // use wid to fetch the eid first + this.workflowPersistService + .retrieveWorkflowEnvironment(this.wid) + .pipe(untilDestroyed(this)) + .subscribe({ + next: env => { + this.environment = env; + this.eid = env.eid; + this.loadDatasetsOfEnvironment(); + this.setEnvironmentTooltip(); + }, + error: (err: unknown) => { + this.notificationService.warning(`Runtime environment of current workflow not found. + Please save current workflow, so that the environment will be created automatically.`); + }, + }); + } + } + + private setEnvironmentTooltip() { + if (this.environment) { + this.environmentTooltip += `Name: ${this.environment.name}\n` + `Description: ${this.environment.description}\n`; + } + } + + private loadDatasetsOfEnvironment() { + this.datasetFileTrees = []; + if (this.eid) { + const eid = this.eid; + this.environmentService.retrieveDatasetsOfEnvironmentDetails(eid).subscribe({ + next: datasets => { + datasets.forEach(entry => { + const did = entry.dataset.did; + const dvid = entry.version.dvid; + if (did && dvid) { + this.datasetService.retrieveDatasetVersionFileTree(did, dvid).subscribe({ + next: datasetFileTree => { + this.datasetsOfEnvironment.set(did, [entry, datasetFileTree]); + this.datasetFileTrees.push([did, entry.dataset.name, datasetFileTree]); + }, + }); + } + }); + }, + error: (err: unknown) => { + this.notificationService.error("Datasets of Environment loading error!"); + }, + }); + } + } + + onClickOpenEnvironmentDatasetDetails(did: number) { + const selectedEntry = this.datasetsOfEnvironment.get(did); + if (selectedEntry) { + this.showingDataset = selectedEntry[0]; + this.showDatasetDetails = true; + } + } + + // related control for dataset link modal + onClickOpenDatasetLinkModal() { + // initialize the datasets info + this.datasetService.retrieveAccessibleDatasets().subscribe({ + next: datasets => { + console.log(datasets); + this.userAccessibleDatasets = datasets.filter(ds => { + const newDid = ds.dataset.did; + return !newDid || !this.datasetsOfEnvironment.has(newDid); + }); + + if (this.userAccessibleDatasets.length == 0) { + this.notificationService.warning("There is no available datasets to be linked to the environment."); + } else { + this.showDatasetLinkModal = true; + } + }, + }); + } + + handleCancelLinkDataset() { + this.showDatasetLinkModal = false; + } + + onClickLinkDataset(dataset: { did: number | undefined; name: string }) { + if (this.eid && dataset.did) { + this.environmentService.addDatasetToEnvironment(this.eid, dataset.did).subscribe({ + next: response => { + this.notificationService.success(`Link dataset ${dataset.name} to the environment successfully`); + this.showDatasetLinkModal = false; + this.loadDatasetsOfEnvironment(); + }, + error: (err: unknown) => { + this.notificationService.error(`Linking dataset ${dataset.name} encounters error`); + }, + }); + } + } + + onUserInputDatasetName(event: Event): void { + const value = this.inputDatasetName; + + if (value) { + this.filteredLinkingDatasets = this.userAccessibleDatasets + .filter(dataset => !dataset.dataset.did || dataset.dataset.name.toLowerCase().includes(value)) + .map(dataset => ({ + name: dataset.dataset.name, + did: dataset.dataset.did, + })); + } + // console.log(this.filteredLinkingDatasetsName) + } + + // controls of dataset details + get showingDatasetName(): string { + if (this.showingDataset?.dataset.name) { + return this.showingDataset.dataset.name; + } + + return ""; + } + + get showingDatasetDid(): string { + const did = this.showingDataset?.dataset.did; + if (did) { + return did.toString(); + } + return ""; + } + + get showingDatasetVersionName(): string { + const versionName = this.showingDataset?.version.name; + if (versionName) { + return versionName; + } + return ""; + } + + get showingDatasetDescription(): string { + const desc = this.showingDataset?.dataset.description; + if (desc) { + return desc; + } + return ""; + } + + handleCancelDatasetDetails() { + this.showDatasetDetails = false; + } + + // controls for displaying dataset file + displayDatasetFileContent(node: DatasetVersionFileTreeNode, did: number) { + const datasetDetails = this.datasetsOfEnvironment.get(did); + if (datasetDetails) { + this.showDatasetFile = true; + this.showingDatasetFile = node; + this.showingDatasetFileDid = did; + this.showingDatasetFileDvid = datasetDetails[0].version.dvid; + } + } + + get selectedDatasetFileDid(): number { + if (this.showingDatasetFileDid) { + return this.showingDatasetFileDid; + } + return 0; + } + + get selectedDatasetFileDvid(): number { + if (this.showingDatasetFileDvid) { + return this.showingDatasetFileDvid; + } + return 0; + } + + get selectedDatasetFilename(): string { + if (this.showingDatasetFile) { + return getFullPathFromFileTreeNode(this.showingDatasetFile); + } + return ""; + } + + handleCancelDatasetFileDisplay() { + this.showDatasetFile = false; + } +} diff --git a/core/new-gui/src/app/workspace/component/left-panel/left-panel.component.ts b/core/new-gui/src/app/workspace/component/left-panel/left-panel.component.ts index 9ed3f50148f..1c2d9f6eafb 100644 --- a/core/new-gui/src/app/workspace/component/left-panel/left-panel.component.ts +++ b/core/new-gui/src/app/workspace/component/left-panel/left-panel.component.ts @@ -6,6 +6,7 @@ import { environment } from "../../../../environments/environment"; import { OperatorMenuComponent } from "./operator-menu/operator-menu.component"; import { VersionsListComponent } from "./versions-list/versions-list.component"; import { TimeTravelComponent } from "./time-travel/time-travel.component"; +import {EnvironmentComponent} from "./environment/environment.component"; @UntilDestroy() @Component({ @@ -30,8 +31,14 @@ export class LeftPanelComponent implements OnDestroy, OnInit { icon: "clock-circle", enabled: environment.userSystemEnabled, }, + { + component: EnvironmentComponent, + title: "Environment", + icon: "dashboard", + enabled: environment.userSystemEnabled + } ]; - order = [1, 2, 3]; + order = [1, 2, 3, 4]; constructor() { const order = localStorage.getItem("left-panel-order"); From 52dda0fed4fc04337412d032318f38f8caa00646 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Sun, 3 Mar 2024 23:02:41 -0800 Subject: [PATCH 02/10] change the autocomplete --- .../input-autocomplete.component.ts | 60 +++++++++++-------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts b/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts index 47ddad23eb2..247aeb817bf 100644 --- a/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts +++ b/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts @@ -1,9 +1,11 @@ import { Component } from "@angular/core"; import { FieldType, FieldTypeConfig } from "@ngx-formly/core"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; -import { UserFileService } from "src/app/dashboard/user/service/user-file/user-file.service"; import { debounceTime } from "rxjs/operators"; import { map } from "rxjs"; +import {WorkflowActionService} from "../../service/workflow-graph/model/workflow-action.service"; +import {EnvironmentService} from "../../../dashboard/user/service/user-environment/environment.service"; +import {WorkflowPersistService} from "../../../common/service/workflow-persist/workflow-persist.service"; @UntilDestroy() @Component({ @@ -15,36 +17,46 @@ export class InputAutoCompleteComponent extends FieldType { // the autocomplete selection list public suggestions: string[] = []; - constructor(public userFileService: UserFileService) { + constructor( + public environmentService: EnvironmentService, + public workflowActionService: WorkflowActionService, + public workflowPersistService: WorkflowPersistService + ) { super(); } autocomplete(): void { - if (this.field.formControl.value === null) { - return; - } // currently it's a hard-code UserFileService autocomplete // TODO: generalize this callback function with a formly hook. const value = this.field.formControl.value.trim(); - if (value.length > 0) { - // perform auto-complete based on the current input - this.userFileService - .getAutoCompleteFileList(value) - .pipe(debounceTime(300)) + const wid = this.workflowActionService.getWorkflowMetadata()?.wid; + if (wid) { + // fetch the wid first + this.workflowPersistService + .retrieveWorkflowEnvironment(wid) .pipe(untilDestroyed(this)) - .subscribe(suggestedFiles => { - const updated = - this.suggestions.length != suggestedFiles.length || - this.suggestions.some((e, i) => e !== suggestedFiles[i]); - if (updated) this.suggestions = [...suggestedFiles]; + .subscribe({ + next: env => { + // then we fetch the file list inorder to do the autocomplete, perform auto-complete based on the current input + const eid = env.eid; + if (eid) { + let query = value; + if (value.length == 0) { + query = ""; + } + this.environmentService + .getDatasetsFileList(eid, query) + .pipe(debounceTime(300)) + .pipe(untilDestroyed(this)) + .subscribe(suggestedFiles => { + // check if there is a difference between new and previous suggestion + const updated = + this.suggestions.length != suggestedFiles.length || + this.suggestions.some((e, i) => e !== suggestedFiles[i]); + if (updated) this.suggestions = [...suggestedFiles]; + }); + } + }, }); - } else { - // no valid input, perform full scan - this.userFileService - .getFileList() - .pipe(map(list => list.map(x => x.ownerEmail + "/" + x.file.name))) - .pipe(untilDestroyed(this)) - .subscribe(allAccessibleFiles => (this.suggestions = allAccessibleFiles)); } - } -} + }} From 1e7ce15c97f9855f5ba9fc954754a0198b79af46 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Mon, 4 Mar 2024 18:03:15 -0800 Subject: [PATCH 03/10] change the autocomplete --- .../GitVersionControlLocalFileStorage.java | 33 ++++++ .../user/dataset/type/DatasetFileDesc.scala | 13 +++ .../environment/EnvironmentResource.scala | 81 ++++++++------ .../user/workflow/WorkflowResource.scala | 20 ++-- .../texera/web/service/WorkflowService.scala | 12 +- .../source/scan/ScanSourceOpDesc.scala | 17 ++- .../ActorVirtualIdentity.scala | 4 +- .../virtualidentity/ChannelIdentity.scala | 4 +- .../ChannelMarkerIdentity.scala | 4 +- .../virtualidentity/EnvironmentIdentity.scala | 105 ++++++++++++++++++ .../virtualidentity/OperatorIdentity.scala | 4 +- .../virtualidentity/PhysicalOpIdentity.scala | 4 +- .../VirtualidentityProto.scala | 21 ++-- .../user-dataset/user-dataset.component.html | 2 +- .../user-dataset/user-dataset.component.ts | 6 +- .../input-autocomplete.component.ts | 2 +- .../environment/environment.component.html | 10 +- .../environment/environment.component.ts | 14 ++- 18 files changed, 255 insertions(+), 101 deletions(-) create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/DatasetFileDesc.scala create mode 100644 core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/EnvironmentIdentity.scala diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorage.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorage.java index d37d6ba728f..b67863d239b 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorage.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorage.java @@ -130,6 +130,39 @@ public static void retrieveFileContentOfVersion(Path baseRepoPath, String commit JGitVersionControl.readFileContentOfCommit(baseRepoPath, commitHash, filePath, outputStream); } + /** + * Creates a temporary file and writes the content of a specific version of a file, identified by its commit hash, into this temporary file. + * This method is useful for retrieving and working with specific versions of a file from a Git repository in a temporary and isolated manner. + * + * The temporary file is created in the system's default temporary-file directory, with a prefix "versionedFile" and a ".tmp" suffix. + * The created temporary file is marked for deletion on JVM exit, ensuring no leftover files during runtime, though it's the caller's responsibility to manage the file if it needs to persist longer. + * + *

This method is THREAD SAFE, ensuring safe use across multiple threads without causing data inconsistency or corruption. + * + * @param baseRepoPath The path to the repository where the file version is to be retrieved from. This should be a valid path to a local repository managed by Git. + * @param commitHash The commit hash that identifies the specific version of the file to retrieve. This commit must exist in the repository's history. + * @param filePath The path of the file within the repository, relative to the repository's root directory. This file should exist in the commit specified. + * @return The {@link Path} to the created temporary file, which contains the content of the specified file version. This path is absolute, ensuring it can be accessed directly. + * @throws IOException If an I/O error occurs during file operations, including issues with creating the temporary file or writing to it. + * @throws GitAPIException If the operation to retrieve file content from the Git repository fails. This could be due to issues with accessing the repository, the commit hash, or the file path specified. + */ + public static Path writeVersionedFileToTempFile(Path baseRepoPath, String commitHash, Path filePath) throws IOException, GitAPIException { + // Generate a temporary file + Path tempFile = Files.createTempFile("versionedFile", ".tmp"); + + // Ensure the file gets deleted on JVM exit + tempFile.toFile().deleteOnExit(); + + // Use the retrieveFileContentOfVersion method to write the file content into the temp file + try (OutputStream outputStream = Files.newOutputStream(tempFile)) { + retrieveFileContentOfVersion(baseRepoPath, commitHash, filePath, outputStream); + } + + // Return the absolute path of the temporary file + return tempFile.toAbsolutePath(); + } + + /** * Check if there is any uncommitted change in the given repo diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/DatasetFileDesc.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/DatasetFileDesc.scala new file mode 100644 index 00000000000..4da7ed632e0 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/DatasetFileDesc.scala @@ -0,0 +1,13 @@ +package edu.uci.ics.texera.web.resource.dashboard.user.dataset.`type` + +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.service.GitVersionControlLocalFileStorage +import java.nio.file.Path + +// This file +class DatasetFileDesc(val fileName: Path, val datasetPath: Path, val versionHash: String) { + def tempFilePath(): Path = { + GitVersionControlLocalFileStorage.writeVersionedFileToTempFile(datasetPath, versionHash, datasetPath.resolve(fileName)) + } + + override def toString: String = s"DatasetFileDesc(fileName=$fileName, datasetPath=$datasetPath, versionHash=$versionHash)" +} \ No newline at end of file diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/environment/EnvironmentResource.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/environment/EnvironmentResource.scala index 74c5fd0d6c0..021bf6313ea 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/environment/EnvironmentResource.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/environment/EnvironmentResource.scala @@ -3,56 +3,28 @@ package edu.uci.ics.texera.web.resource.dashboard.user.environment import edu.uci.ics.texera.Utils.withTransaction import edu.uci.ics.texera.web.SqlServer import edu.uci.ics.texera.web.auth.SessionUser -import edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.{ - Dataset, - DatasetOfEnvironment, - DatasetVersion, - Environment -} +import edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.{Dataset, DatasetOfEnvironment, DatasetVersion, Environment} import edu.uci.ics.texera.web.model.jooq.generated.tables.Environment.ENVIRONMENT import edu.uci.ics.texera.web.model.jooq.generated.tables.EnvironmentOfWorkflow.ENVIRONMENT_OF_WORKFLOW import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetOfEnvironment.DATASET_OF_ENVIRONMENT -import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{ - DatasetDao, - DatasetOfEnvironmentDao, - DatasetVersionDao, - EnvironmentDao, - EnvironmentOfWorkflowDao -} +import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{DatasetDao, DatasetOfEnvironmentDao, DatasetVersionDao, EnvironmentDao, EnvironmentOfWorkflowDao} import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetResource.retrieveDatasetVersionFilePaths -import edu.uci.ics.texera.web.resource.dashboard.user.dataset.{ - DatasetAccessResource, - DatasetResource -} -import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource.{ - DashboardEnvironment, - DatasetID, - DatasetOfEnvironmentAlreadyExistsMessage, - DatasetOfEnvironmentDetails, - DatasetOfEnvironmentDoseNotExistMessage, - EnvironmentIDs, - UserNoPermissionExceptionMessage, - WorkflowLink, - context, - doesDatasetExistInEnvironment, - doesUserOwnEnvironment, - getEnvironmentByEid, - retrieveDatasetsAndVersions, - retrieveDatasetsOfEnvironmentFileList, - userHasReadAccessToEnvironment, - userHasWriteAccessToEnvironment -} +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.`type`.DatasetFileDesc +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.{DatasetAccessResource, DatasetResource} +import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource.{DashboardEnvironment, DatasetID, DatasetOfEnvironmentAlreadyExistsMessage, DatasetOfEnvironmentDetails, DatasetOfEnvironmentDoseNotExistMessage, EnvironmentIDs, UserNoPermissionExceptionMessage, WorkflowLink, context, doesDatasetExistInEnvironment, doesUserOwnEnvironment, getEnvironmentByEid, retrieveDatasetsAndVersions, retrieveDatasetsOfEnvironmentFileList, userHasReadAccessToEnvironment, userHasWriteAccessToEnvironment} import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowAccessResource import io.dropwizard.auth.Auth import org.jooq.DSLContext import org.jooq.types.UInteger import java.net.URLDecoder +import java.nio.file.Paths import javax.annotation.security.RolesAllowed import javax.ws.rs.core.{MediaType, Response} import javax.ws.rs.{GET, POST, Path, PathParam, Produces} import scala.collection.mutable.{ArrayBuffer, ListBuffer} import scala.jdk.CollectionConverters.CollectionHasAsScala +import scala.util.matching.Regex object EnvironmentResource { private val context = SqlServer.createDSLContext() @@ -94,6 +66,43 @@ object EnvironmentResource { .into(classOf[Environment]) } + // return the descriptor of the target file. + // The filename is passed from the frontend, the did is contained in the filename in the format of /{dataset-name}/{filepath} + def getEnvironmentDatasetFilePathAndVersion(uid: UInteger, eid: UInteger, fileName: String): DatasetFileDesc = { + withTransaction(context) { ctx => { + // Adjust the pattern to match the new fileName format + val datasetNamePattern: Regex = """/([^/]+)/.*""".r + + // Extract 'datasetName' using the pattern + val datasetName = datasetNamePattern.findFirstMatchIn(fileName) match { + case Some(matched) => matched.group(1) // Extract the first group which is 'datasetName' + case None => throw new RuntimeException("The fileName format is not correct") // Default value or handle error + } + + // Extract the file path + val filePath = Paths.get(fileName.substring(fileName.indexOf(s"/$datasetName/") + s"/$datasetName/".length)) + val datasetsOfEnvironment = retrieveDatasetsAndVersions(ctx, uid, eid) + + // Initialize datasetFileDesc as None + var datasetFileDesc: Option[DatasetFileDesc] = None + + // Iterate over datasetsOfEnvironment to find a match based on datasetName + datasetsOfEnvironment.foreach { datasetAndVersion => + if (datasetAndVersion.dataset.getName == datasetName) { + datasetFileDesc = Some(new DatasetFileDesc( + filePath, + Paths.get(datasetAndVersion.dataset.getStoragePath), + datasetAndVersion.version.getVersionHash)) + } + } + + // Check if datasetFileDesc is set, if not, throw an exception + datasetFileDesc.getOrElse(throw new RuntimeException("Given file is not found in the environment")) + } + } + } + + private def getEnvironmentByEid(ctx: DSLContext, eid: UInteger): Environment = { val environmentDao: EnvironmentDao = new EnvironmentDao(ctx.configuration()) val env = environmentDao.fetchOneByEid(eid) @@ -226,7 +235,7 @@ object EnvironmentResource { val datasetName = entry.dataset.getName val fileList = retrieveDatasetVersionFilePaths(ctx, uid, did, dvid) val resList: ListBuffer[String] = new ListBuffer[String] - fileList.forEach(file => resList.append(s"/$datasetName-$did/$file")) + fileList.forEach(file => resList.append(s"/$datasetName/$file")) resList.toList }) } diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala index 18b0bbd141d..958409cf537 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala @@ -5,23 +5,14 @@ import edu.uci.ics.texera.web.SqlServer import edu.uci.ics.texera.web.auth.SessionUser import edu.uci.ics.texera.web.model.jooq.generated.Tables._ import edu.uci.ics.texera.web.model.jooq.generated.enums.WorkflowUserAccessPrivilege -import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{ - EnvironmentOfWorkflowDao, - WorkflowDao, - WorkflowOfProjectDao, - WorkflowOfUserDao, - WorkflowUserAccessDao -} +import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{EnvironmentOfWorkflowDao, WorkflowDao, WorkflowOfProjectDao, WorkflowOfUserDao, WorkflowUserAccessDao} import edu.uci.ics.texera.web.model.jooq.generated.tables.pojos._ import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource -import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource.{ - createEnvironment, - doesWorkflowHaveEnvironment -} +import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource.{createEnvironment, doesWorkflowHaveEnvironment} import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowAccessResource.hasReadAccess import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowResource._ import io.dropwizard.auth.Auth -import org.jooq.Condition +import org.jooq.{Condition, DSLContext} import org.jooq.impl.DSL.{groupConcatDistinct, noCondition} import org.jooq.types.UInteger @@ -82,6 +73,11 @@ object WorkflowResource { ) } + def getEnvironmentEidOfWorkflow(wid: UInteger): UInteger = { + val environmentOfWorkflow = environmentOfWorkflowDao.fetchByWid(wid) + environmentOfWorkflow.get(0).getEid + } + case class DashboardWorkflow( isOwner: Boolean, accessLevel: String, diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala index 5a69751c749..26499e43d8d 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala @@ -3,18 +3,12 @@ package edu.uci.ics.texera.web.service import com.google.protobuf.timestamp.Timestamp import com.typesafe.scalalogging.LazyLogging import edu.uci.ics.amber.engine.architecture.controller.ControllerConfig -import edu.uci.ics.amber.engine.architecture.worker.WorkflowWorker.{ - FaultToleranceConfig, - StateRestoreConfig -} +import edu.uci.ics.amber.engine.architecture.worker.WorkflowWorker.{FaultToleranceConfig, StateRestoreConfig} import edu.uci.ics.amber.engine.common.AmberConfig -import edu.uci.ics.amber.engine.common.virtualidentity.{ - ChannelMarkerIdentity, - ExecutionIdentity, - WorkflowIdentity -} +import edu.uci.ics.amber.engine.common.virtualidentity.{ChannelMarkerIdentity, EnvironmentIdentity, ExecutionIdentity, WorkflowIdentity} import edu.uci.ics.texera.web.model.websocket.event.TexeraWebSocketEvent import edu.uci.ics.texera.web.model.websocket.request.WorkflowExecuteRequest +import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowResource import edu.uci.ics.texera.web.service.WorkflowService.mkWorkflowStateId import edu.uci.ics.texera.web.storage.ExecutionStateStore.updateWorkflowState import edu.uci.ics.texera.web.storage.{ExecutionStateStore, WorkflowStateStore} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala b/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala index cc8b6ceb490..f16d36b0440 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala @@ -4,7 +4,9 @@ import com.fasterxml.jackson.annotation.{JsonIgnore, JsonProperty, JsonPropertyD import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle import edu.uci.ics.amber.engine.common.workflow.OutputPort +import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource.getEnvironmentDatasetFilePathAndVersion import edu.uci.ics.texera.web.resource.dashboard.user.file.UserFileAccessResource +import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowResource import edu.uci.ics.texera.workflow.common.WorkflowContext import edu.uci.ics.texera.workflow.common.metadata.{OperatorGroupConstants, OperatorInfo} import edu.uci.ics.texera.workflow.common.operators.source.SourceOperatorDescriptor @@ -63,17 +65,12 @@ abstract class ScanSourceOpDesc extends SourceOperatorDescriptor { if (getContext.userId.isDefined) { // if context has a valid user ID, the fileName will be in the following format: - // ownerName/fileName + // /datasetName/fileName // resolve fileName to be the actual file path. - val splitNames = fileName.get.split("/") - filePath = UserFileAccessResource - .getFilePath( - email = splitNames.apply(0), - fileName = splitNames.apply(1), - getContext.userId.get, - UInteger.valueOf(getContext.workflowId.id) - ) - + // fetch the environment id that workflow is in + val environmentEid = WorkflowResource.getEnvironmentEidOfWorkflow(UInteger.valueOf(workflowContext.workflowId.id)) + val datasetFileDescriptor = getEnvironmentDatasetFilePathAndVersion(getContext.userId.get, environmentEid, fileName.get) + filePath = Some(datasetFileDescriptor.tempFilePath().toString) } else { // otherwise, the fileName will be inputted by user, which is the filePath. filePath = fileName diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ActorVirtualIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ActorVirtualIdentity.scala index c8db20851ef..c41feae4785 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ActorVirtualIdentity.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ActorVirtualIdentity.scala @@ -84,8 +84,8 @@ object ActorVirtualIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.ic ) case _ => throw new RuntimeException("Expected PMessage") } - def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(2) - def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(2) + def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(3) + def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(3) def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = throw new MatchError(__number) lazy val nestedMessagesCompanions: Seq[_root_.scalapb.GeneratedMessageCompanion[_ <: _root_.scalapb.GeneratedMessage]] = Seq.empty def enumCompanionForFieldNumber(__fieldNumber: _root_.scala.Int): _root_.scalapb.GeneratedEnumCompanion[_] = throw new MatchError(__fieldNumber) diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelIdentity.scala index 8297da89498..9bc4991c8f5 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelIdentity.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelIdentity.scala @@ -138,8 +138,8 @@ object ChannelIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.ics.amb ) case _ => throw new RuntimeException("Expected PMessage") } - def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(3) - def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(3) + def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(4) + def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(4) def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = { var __out: _root_.scalapb.GeneratedMessageCompanion[_] = null (__number: @_root_.scala.unchecked) match { diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelMarkerIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelMarkerIdentity.scala index f9cce189278..a799058f2fc 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelMarkerIdentity.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelMarkerIdentity.scala @@ -84,8 +84,8 @@ object ChannelMarkerIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.i ) case _ => throw new RuntimeException("Expected PMessage") } - def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(6) - def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(6) + def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(7) + def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(7) def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = throw new MatchError(__number) lazy val nestedMessagesCompanions: Seq[_root_.scalapb.GeneratedMessageCompanion[_ <: _root_.scalapb.GeneratedMessage]] = Seq.empty def enumCompanionForFieldNumber(__fieldNumber: _root_.scala.Int): _root_.scalapb.GeneratedEnumCompanion[_] = throw new MatchError(__fieldNumber) diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/EnvironmentIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/EnvironmentIdentity.scala new file mode 100644 index 00000000000..6086fad07f9 --- /dev/null +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/EnvironmentIdentity.scala @@ -0,0 +1,105 @@ +// Generated by the Scala Plugin for the Protocol Buffer Compiler. +// Do not edit! +// +// Protofile syntax: PROTO3 + +package edu.uci.ics.amber.engine.common.virtualidentity + +@SerialVersionUID(0L) +final case class EnvironmentIdentity( + id: _root_.scala.Long + ) extends scalapb.GeneratedMessage with scalapb.lenses.Updatable[EnvironmentIdentity] { + @transient + private[this] var __serializedSizeCachedValue: _root_.scala.Int = 0 + private[this] def __computeSerializedValue(): _root_.scala.Int = { + var __size = 0 + + { + val __value = id + if (__value != 0L) { + __size += _root_.com.google.protobuf.CodedOutputStream.computeInt64Size(1, __value) + } + }; + __size + } + override def serializedSize: _root_.scala.Int = { + var read = __serializedSizeCachedValue + if (read == 0) { + read = __computeSerializedValue() + __serializedSizeCachedValue = read + } + read + } + def writeTo(`_output__`: _root_.com.google.protobuf.CodedOutputStream): _root_.scala.Unit = { + { + val __v = id + if (__v != 0L) { + _output__.writeInt64(1, __v) + } + }; + } + def withId(__v: _root_.scala.Long): EnvironmentIdentity = copy(id = __v) + def getFieldByNumber(__fieldNumber: _root_.scala.Int): _root_.scala.Any = { + (__fieldNumber: @_root_.scala.unchecked) match { + case 1 => { + val __t = id + if (__t != 0L) __t else null + } + } + } + def getField(__field: _root_.scalapb.descriptors.FieldDescriptor): _root_.scalapb.descriptors.PValue = { + _root_.scala.Predef.require(__field.containingMessage eq companion.scalaDescriptor) + (__field.number: @_root_.scala.unchecked) match { + case 1 => _root_.scalapb.descriptors.PLong(id) + } + } + def toProtoString: _root_.scala.Predef.String = _root_.scalapb.TextFormat.printToSingleLineUnicodeString(this) + def companion = edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity + // @@protoc_insertion_point(GeneratedMessage[edu.uci.ics.amber.engine.common.EnvironmentIdentity]) +} + +object EnvironmentIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity] { + implicit def messageCompanion: scalapb.GeneratedMessageCompanion[edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity] = this + def parseFrom(`_input__`: _root_.com.google.protobuf.CodedInputStream): edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity = { + var __id: _root_.scala.Long = 0L + var _done__ = false + while (!_done__) { + val _tag__ = _input__.readTag() + _tag__ match { + case 0 => _done__ = true + case 8 => + __id = _input__.readInt64() + case tag => _input__.skipField(tag) + } + } + edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity( + id = __id + ) + } + implicit def messageReads: _root_.scalapb.descriptors.Reads[edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity] = _root_.scalapb.descriptors.Reads{ + case _root_.scalapb.descriptors.PMessage(__fieldsMap) => + _root_.scala.Predef.require(__fieldsMap.keys.forall(_.containingMessage eq scalaDescriptor), "FieldDescriptor does not match message type.") + edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity( + id = __fieldsMap.get(scalaDescriptor.findFieldByNumber(1).get).map(_.as[_root_.scala.Long]).getOrElse(0L) + ) + case _ => throw new RuntimeException("Expected PMessage") + } + def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(2) + def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(2) + def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = throw new MatchError(__number) + lazy val nestedMessagesCompanions: Seq[_root_.scalapb.GeneratedMessageCompanion[_ <: _root_.scalapb.GeneratedMessage]] = Seq.empty + def enumCompanionForFieldNumber(__fieldNumber: _root_.scala.Int): _root_.scalapb.GeneratedEnumCompanion[_] = throw new MatchError(__fieldNumber) + lazy val defaultInstance = edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity( + id = 0L + ) + implicit class EnvironmentIdentityLens[UpperPB](_l: _root_.scalapb.lenses.Lens[UpperPB, edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity]) extends _root_.scalapb.lenses.ObjectLens[UpperPB, edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity](_l) { + def id: _root_.scalapb.lenses.Lens[UpperPB, _root_.scala.Long] = field(_.id)((c_, f_) => c_.copy(id = f_)) + } + final val ID_FIELD_NUMBER = 1 + def of( + id: _root_.scala.Long + ): _root_.edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity = _root_.edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity( + id + ) + // @@protoc_insertion_point(GeneratedMessageCompanion[edu.uci.ics.amber.engine.common.EnvironmentIdentity]) +} diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/OperatorIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/OperatorIdentity.scala index b1b79dd7884..2fa77f74591 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/OperatorIdentity.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/OperatorIdentity.scala @@ -84,8 +84,8 @@ object OperatorIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.ics.am ) case _ => throw new RuntimeException("Expected PMessage") } - def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(4) - def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(4) + def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(5) + def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(5) def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = throw new MatchError(__number) lazy val nestedMessagesCompanions: Seq[_root_.scalapb.GeneratedMessageCompanion[_ <: _root_.scalapb.GeneratedMessage]] = Seq.empty def enumCompanionForFieldNumber(__fieldNumber: _root_.scala.Int): _root_.scalapb.GeneratedEnumCompanion[_] = throw new MatchError(__fieldNumber) diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/PhysicalOpIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/PhysicalOpIdentity.scala index 3075b6e8cda..233286da588 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/PhysicalOpIdentity.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/PhysicalOpIdentity.scala @@ -111,8 +111,8 @@ object PhysicalOpIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.ics. ) case _ => throw new RuntimeException("Expected PMessage") } - def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(5) - def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(5) + def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(6) + def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(6) def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = { var __out: _root_.scalapb.GeneratedMessageCompanion[_] = null (__number: @_root_.scala.unchecked) match { diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/VirtualidentityProto.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/VirtualidentityProto.scala index 7f75701f4d7..d211d3d0e03 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/VirtualidentityProto.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/VirtualidentityProto.scala @@ -13,6 +13,7 @@ object VirtualidentityProto extends _root_.scalapb.GeneratedFileObject { Seq[_root_.scalapb.GeneratedMessageCompanion[_ <: _root_.scalapb.GeneratedMessage]]( edu.uci.ics.amber.engine.common.virtualidentity.WorkflowIdentity, edu.uci.ics.amber.engine.common.virtualidentity.ExecutionIdentity, + edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity, edu.uci.ics.amber.engine.common.virtualidentity.ActorVirtualIdentity, edu.uci.ics.amber.engine.common.virtualidentity.ChannelIdentity, edu.uci.ics.amber.engine.common.virtualidentity.OperatorIdentity, @@ -23,16 +24,16 @@ object VirtualidentityProto extends _root_.scalapb.GeneratedFileObject { scalapb.Encoding.fromBase64(scala.collection.immutable.Seq( """CjVlZHUvdWNpL2ljcy9hbWJlci9lbmdpbmUvY29tbW9uL3ZpcnR1YWxpZGVudGl0eS5wcm90bxIfZWR1LnVjaS5pY3MuYW1iZ XIuZW5naW5lLmNvbW1vbhoVc2NhbGFwYi9zY2FsYXBiLnByb3RvIisKEFdvcmtmbG93SWRlbnRpdHkSFwoCaWQYASABKANCB+I/B - BICaWRSAmlkIiwKEUV4ZWN1dGlvbklkZW50aXR5EhcKAmlkGAEgASgDQgfiPwQSAmlkUgJpZCI1ChRBY3RvclZpcnR1YWxJZGVud - Gl0eRIdCgRuYW1lGAEgASgJQgniPwYSBG5hbWVSBG5hbWUimwIKD0NoYW5uZWxJZGVudGl0eRJvCgxmcm9tV29ya2VySWQYASABK - AsyNS5lZHUudWNpLmljcy5hbWJlci5lbmdpbmUuY29tbW9uLkFjdG9yVmlydHVhbElkZW50aXR5QhTiPxESDGZyb21Xb3JrZXJJZ - PABAVIMZnJvbVdvcmtlcklkEmkKCnRvV29ya2VySWQYAiABKAsyNS5lZHUudWNpLmljcy5hbWJlci5lbmdpbmUuY29tbW9uLkFjd - G9yVmlydHVhbElkZW50aXR5QhLiPw8SCnRvV29ya2VySWTwAQFSCnRvV29ya2VySWQSLAoJaXNDb250cm9sGAMgASgIQg7iPwsSC - WlzQ29udHJvbFIJaXNDb250cm9sIisKEE9wZXJhdG9ySWRlbnRpdHkSFwoCaWQYASABKAlCB+I/BBICaWRSAmlkIqwBChJQaHlza - WNhbE9wSWRlbnRpdHkSaAoLbG9naWNhbE9wSWQYASABKAsyMS5lZHUudWNpLmljcy5hbWJlci5lbmdpbmUuY29tbW9uLk9wZXJhd - G9ySWRlbnRpdHlCE+I/EBILbG9naWNhbE9wSWTwAQFSC2xvZ2ljYWxPcElkEiwKCWxheWVyTmFtZRgCIAEoCUIO4j8LEglsYXllc - k5hbWVSCWxheWVyTmFtZSIwChVDaGFubmVsTWFya2VySWRlbnRpdHkSFwoCaWQYASABKAlCB+I/BBICaWRSAmlkQgniPwZIAFgAe - AFiBnByb3RvMw==""" + BICaWRSAmlkIiwKEUV4ZWN1dGlvbklkZW50aXR5EhcKAmlkGAEgASgDQgfiPwQSAmlkUgJpZCIuChNFbnZpcm9ubWVudElkZW50a + XR5EhcKAmlkGAEgASgDQgfiPwQSAmlkUgJpZCI1ChRBY3RvclZpcnR1YWxJZGVudGl0eRIdCgRuYW1lGAEgASgJQgniPwYSBG5hb + WVSBG5hbWUimwIKD0NoYW5uZWxJZGVudGl0eRJvCgxmcm9tV29ya2VySWQYASABKAsyNS5lZHUudWNpLmljcy5hbWJlci5lbmdpb + mUuY29tbW9uLkFjdG9yVmlydHVhbElkZW50aXR5QhTiPxESDGZyb21Xb3JrZXJJZPABAVIMZnJvbVdvcmtlcklkEmkKCnRvV29ya + 2VySWQYAiABKAsyNS5lZHUudWNpLmljcy5hbWJlci5lbmdpbmUuY29tbW9uLkFjdG9yVmlydHVhbElkZW50aXR5QhLiPw8SCnRvV + 29ya2VySWTwAQFSCnRvV29ya2VySWQSLAoJaXNDb250cm9sGAMgASgIQg7iPwsSCWlzQ29udHJvbFIJaXNDb250cm9sIisKEE9wZ + XJhdG9ySWRlbnRpdHkSFwoCaWQYASABKAlCB+I/BBICaWRSAmlkIqwBChJQaHlzaWNhbE9wSWRlbnRpdHkSaAoLbG9naWNhbE9wS + WQYASABKAsyMS5lZHUudWNpLmljcy5hbWJlci5lbmdpbmUuY29tbW9uLk9wZXJhdG9ySWRlbnRpdHlCE+I/EBILbG9naWNhbE9wS + WTwAQFSC2xvZ2ljYWxPcElkEiwKCWxheWVyTmFtZRgCIAEoCUIO4j8LEglsYXllck5hbWVSCWxheWVyTmFtZSIwChVDaGFubmVsT + WFya2VySWRlbnRpdHkSFwoCaWQYASABKAlCB+I/BBICaWRSAmlkQgniPwZIAFgAeAFiBnByb3RvMw==""" ).mkString) lazy val scalaDescriptor: _root_.scalapb.descriptors.FileDescriptor = { val scalaProto = com.google.protobuf.descriptor.FileDescriptorProto.parseFrom(ProtoBytes) diff --git a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset.component.html b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset.component.html index fb757ad0a8e..325a852ea00 100644 --- a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset.component.html +++ b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset.component.html @@ -56,7 +56,7 @@

Datasets

diff --git a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset.component.ts b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset.component.ts index f323ed211a8..2d7b001a07a 100644 --- a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset.component.ts +++ b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset.component.ts @@ -81,10 +81,10 @@ export class UserDatasetComponent implements OnInit { return datasetArray; } - public deleteDataset(entry: DashboardEntry) { - if (entry.dataset.dataset.did) { + public deleteDataset(entry: DashboardDataset) { + if (entry.dataset.did) { this.datasetService - .deleteDatasets([entry.dataset.dataset.did]) + .deleteDatasets([entry.dataset.did]) .pipe(untilDestroyed(this)) .subscribe(_ => { this.reloadDashboardDatasetEntries(); diff --git a/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts b/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts index 247aeb817bf..089c7a525f0 100644 --- a/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts +++ b/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts @@ -26,7 +26,7 @@ export class InputAutoCompleteComponent extends FieldType { } autocomplete(): void { - // currently it's a hard-code UserFileService autocomplete + // currently it's a hard-code DatasetFile autocomplete // TODO: generalize this callback function with a formly hook. const value = this.field.formControl.value.trim(); const wid = this.workflowActionService.getWorkflowMetadata()?.wid; diff --git a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.html b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.html index 7b8cc58887e..a16e0bae48d 100644 --- a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.html +++ b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.html @@ -29,8 +29,8 @@ class="link-dataset-btn" nz-button nzType="primary" - (click)="onClickOpenDatasetLinkModal()"> - Link Dataset + (click)="onClickOpenDatasetAddModal()"> + Add Dataset @@ -58,7 +58,7 @@ @@ -83,8 +83,8 @@ nz-button nzType="primary" class="dataset-option-link-btn" - (click)="onClickLinkDataset(dataset)"> - Link + (click)="onClickAddDataset(dataset)"> + Add diff --git a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts index 1a5e6fbf3d3..af4e21c17f7 100644 --- a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts +++ b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts @@ -126,18 +126,23 @@ export class EnvironmentComponent implements OnInit { } // related control for dataset link modal - onClickOpenDatasetLinkModal() { + onClickOpenDatasetAddModal() { // initialize the datasets info this.datasetService.retrieveAccessibleDatasets().subscribe({ next: datasets => { console.log(datasets); this.userAccessibleDatasets = datasets.filter(ds => { const newDid = ds.dataset.did; - return !newDid || !this.datasetsOfEnvironment.has(newDid); + return newDid && !this.datasetsOfEnvironment.has(newDid); }); + this.filteredLinkingDatasets = this.userAccessibleDatasets + .map(dataset => ({ + name: dataset.dataset.name, + did: dataset.dataset.did, + })) if (this.userAccessibleDatasets.length == 0) { - this.notificationService.warning("There is no available datasets to be linked to the environment."); + this.notificationService.warning("There is no available datasets to be added to the environment."); } else { this.showDatasetLinkModal = true; } @@ -149,7 +154,7 @@ export class EnvironmentComponent implements OnInit { this.showDatasetLinkModal = false; } - onClickLinkDataset(dataset: { did: number | undefined; name: string }) { + onClickAddDataset(dataset: { did: number | undefined; name: string }) { if (this.eid && dataset.did) { this.environmentService.addDatasetToEnvironment(this.eid, dataset.did).subscribe({ next: response => { @@ -178,6 +183,7 @@ export class EnvironmentComponent implements OnInit { // console.log(this.filteredLinkingDatasetsName) } + // controls of dataset details get showingDatasetName(): string { if (this.showingDataset?.dataset.name) { From 8cfd683a26ec02399ee77a4813408d362c364606 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Mon, 4 Mar 2024 18:28:33 -0800 Subject: [PATCH 04/10] screen out the duplicate name --- .../environment/environment.component.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts index af4e21c17f7..610223c6b9c 100644 --- a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts +++ b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts @@ -133,13 +133,20 @@ export class EnvironmentComponent implements OnInit { console.log(datasets); this.userAccessibleDatasets = datasets.filter(ds => { const newDid = ds.dataset.did; - return newDid && !this.datasetsOfEnvironment.has(newDid); + const newName = ds.dataset.name; + + // Check if the datasetsOfEnvironment does not have the newDid + const didNotExist = newDid && !this.datasetsOfEnvironment.has(newDid); + + // Check if the datasetsOfEnvironment does not have the newName + const nameNotExist = ![...this.datasetsOfEnvironment.values()].some(([details, _]) => details.dataset.name === newName); + return didNotExist && nameNotExist; }); - this.filteredLinkingDatasets = this.userAccessibleDatasets - .map(dataset => ({ - name: dataset.dataset.name, - did: dataset.dataset.did, - })) + + this.filteredLinkingDatasets = this.userAccessibleDatasets.map(dataset => ({ + name: dataset.dataset.name, + did: dataset.dataset.did, + })); if (this.userAccessibleDatasets.length == 0) { this.notificationService.warning("There is no available datasets to be added to the environment."); From 1b2cb703dcc22e96af6db88a8a0fa538fadd87ec Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Mon, 4 Mar 2024 19:39:53 -0800 Subject: [PATCH 05/10] fix share access --- .../dashboard/DatasetSearchQueryBuilder.scala | 6 +- .../user/dataset/DatasetResource.scala | 73 +++++++------------ .../user-dataset-version-creator.component.ts | 2 + 3 files changed, 32 insertions(+), 49 deletions(-) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala index 098ce69897d..19140ef40f4 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala @@ -29,6 +29,8 @@ object DatasetSearchQueryBuilder extends SearchQueryBuilder { datasetUserAccess = DATASET_USER_ACCESS.PRIVILEGE ) + // Notice that this only select those datasets that users have access record. + // For those public datasets, need external union to merge them override protected def constructFromClause( uid: UInteger, params: DashboardResource.SearchQueryParams @@ -37,9 +39,7 @@ object DatasetSearchQueryBuilder extends SearchQueryBuilder { .leftJoin(DATASET_USER_ACCESS) .on(DATASET_USER_ACCESS.DID.eq(DATASET.DID)) .where( - DATASET.IS_PUBLIC - .eq(DatasetResource.DATASET_IS_PUBLIC) - .or(DATASET_USER_ACCESS.UID.eq(uid)) + DATASET_USER_ACCESS.UID.eq(uid) ) } diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala index d84d6977cd8..76b7a3c8fe7 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala @@ -4,43 +4,14 @@ import edu.uci.ics.texera.Utils.withTransaction import edu.uci.ics.texera.web.SqlServer import edu.uci.ics.texera.web.auth.SessionUser import edu.uci.ics.texera.web.model.jooq.generated.enums.DatasetUserAccessPrivilege -import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{ - DatasetDao, - DatasetUserAccessDao, - DatasetVersionDao -} -import edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.{ - Dataset, - DatasetUserAccess, - DatasetVersion -} +import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{DatasetDao, DatasetUserAccessDao, DatasetVersionDao} +import edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.{Dataset, DatasetUserAccess, DatasetVersion} import edu.uci.ics.texera.web.model.jooq.generated.tables.Dataset.DATASET import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetVersion.DATASET_VERSION import edu.uci.ics.texera.web.resource.dashboard.DashboardResource import edu.uci.ics.texera.web.resource.dashboard.DashboardResource.SearchQueryParams -import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetAccessResource.{ - getDatasetUserAccessPrivilege, - userHasReadAccess, - userHasWriteAccess, - userOwnDataset -} -import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetResource.{ - DashboardDataset, - DashboardDatasetVersion, - DatasetDescriptionModification, - DatasetIDs, - DatasetNameModification, - DatasetVersionRootFileNodes, - DatasetVersions, - ERR_DATASET_CREATION_FAILED_MESSAGE, - ERR_USER_HAS_NO_ACCESS_TO_DATASET_MESSAGE, - context, - createNewDatasetVersion, - getDashboardDataset, - getDatasetByID, - getDatasetLatestVersion, - getDatasetVersionHashByID -} +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetAccessResource.{getDatasetUserAccessPrivilege, userHasReadAccess, userHasWriteAccess, userOwnDataset} +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetResource.{DashboardDataset, DashboardDatasetVersion, DatasetDescriptionModification, DatasetIDs, DatasetNameModification, DatasetVersionRootFileNodes, DatasetVersions, ERR_DATASET_CREATION_FAILED_MESSAGE, ERR_USER_HAS_NO_ACCESS_TO_DATASET_MESSAGE, context, createNewDatasetVersion, getDashboardDataset, getDatasetByID, getDatasetLatestVersion, getDatasetVersionHashByID, retrievePublicDatasets} import edu.uci.ics.texera.web.resource.dashboard.user.dataset.`type`.FileNode import edu.uci.ics.texera.web.resource.dashboard.user.dataset.service.GitVersionControlLocalFileStorage import edu.uci.ics.texera.web.resource.dashboard.user.dataset.utils.PathUtils @@ -56,18 +27,7 @@ import java.nio.file.Paths import java.util import java.util.concurrent.locks.ReentrantLock import javax.annotation.security.RolesAllowed -import javax.ws.rs.{ - BadRequestException, - Consumes, - ForbiddenException, - GET, - NotFoundException, - POST, - Path, - PathParam, - Produces, - QueryParam -} +import javax.ws.rs.{BadRequestException, Consumes, ForbiddenException, GET, NotFoundException, POST, Path, PathParam, Produces, QueryParam} import javax.ws.rs.core.{MediaType, Response, StreamingOutput} import scala.jdk.CollectionConverters._ @@ -273,6 +233,12 @@ object DatasetResource { FileNode.getAllFileRelativePaths(fileNodes) } + def retrievePublicDatasets( + ctx: DSLContext): util.List[Dataset] = { + val datasetDao = new DatasetDao(ctx.configuration()) + datasetDao.fetchByIsPublic(DATASET_IS_PUBLIC) + } + case class DashboardDataset( dataset: Dataset, accessPrivilege: EnumType, @@ -492,7 +458,22 @@ class DatasetResource { user, SearchQueryParams(resourceType = "dataset") ) - result.results.map(_.dataset.get) + var accessibleDatasets = result.results.map(_.dataset.get) + val publicDatasets = retrievePublicDatasets(context) + + publicDatasets.forEach { publicDataset => + if (!accessibleDatasets.exists(_.dataset.getDid == publicDataset.getDid)) { + // Assuming DashboardDataset has a property did for comparison + val dashboardDataset = DashboardDataset( + isOwner = false, + dataset = publicDataset, + accessPrivilege = DatasetUserAccessPrivilege.READ + ) + accessibleDatasets = accessibleDatasets :+ dashboardDataset + } + } + + accessibleDatasets } @GET diff --git a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.ts b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.ts index 755c397b81e..68c95d58c91 100644 --- a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.ts +++ b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.ts @@ -71,6 +71,7 @@ export class UserDatasetVersionCreatorComponent implements OnInit { { key: "description", type: "input", + defaultValue: "", templateOptions: { label: "Description", }, @@ -78,6 +79,7 @@ export class UserDatasetVersionCreatorComponent implements OnInit { { key: "versionName", type: "input", + defaultValue: "v1", templateOptions: { label: "Initial Version Name", required: true, From 4194d9096193d62617e6431228f0e2f2aa4dd024 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Mon, 4 Mar 2024 23:08:48 -0800 Subject: [PATCH 06/10] fix workflow executions --- .../user/dataset/DatasetResource.scala | 30 ++++- .../ExecutionsMetadataPersistService.scala | 5 +- .../texera/web/service/WorkflowService.scala | 7 +- .../ActorVirtualIdentity.scala | 4 +- .../virtualidentity/ChannelIdentity.scala | 4 +- .../ChannelMarkerIdentity.scala | 4 +- .../virtualidentity/EnvironmentIdentity.scala | 105 ------------------ .../virtualidentity/OperatorIdentity.scala | 4 +- .../virtualidentity/PhysicalOpIdentity.scala | 4 +- .../VirtualidentityProto.scala | 21 ++-- .../user-dataset-explorer.component.html | 8 ++ .../user-dataset-explorer.component.ts | 23 ++++ .../service/user-dataset/dataset.service.ts | 5 + .../environment/environment.component.ts | 1 - 14 files changed, 93 insertions(+), 132 deletions(-) delete mode 100644 core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/EnvironmentIdentity.scala diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala index 76b7a3c8fe7..bb1ef5640e2 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala @@ -11,7 +11,7 @@ import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetVersion.DATASET import edu.uci.ics.texera.web.resource.dashboard.DashboardResource import edu.uci.ics.texera.web.resource.dashboard.DashboardResource.SearchQueryParams import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetAccessResource.{getDatasetUserAccessPrivilege, userHasReadAccess, userHasWriteAccess, userOwnDataset} -import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetResource.{DashboardDataset, DashboardDatasetVersion, DatasetDescriptionModification, DatasetIDs, DatasetNameModification, DatasetVersionRootFileNodes, DatasetVersions, ERR_DATASET_CREATION_FAILED_MESSAGE, ERR_USER_HAS_NO_ACCESS_TO_DATASET_MESSAGE, context, createNewDatasetVersion, getDashboardDataset, getDatasetByID, getDatasetLatestVersion, getDatasetVersionHashByID, retrievePublicDatasets} +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetResource.{DATASET_IS_PRIVATE, DATASET_IS_PUBLIC, DashboardDataset, DashboardDatasetVersion, DatasetDescriptionModification, DatasetIDs, DatasetNameModification, DatasetVersionRootFileNodes, DatasetVersions, ERR_DATASET_CREATION_FAILED_MESSAGE, ERR_USER_HAS_NO_ACCESS_TO_DATASET_MESSAGE, context, createNewDatasetVersion, getDashboardDataset, getDatasetByID, getDatasetLatestVersion, getDatasetVersionHashByID, retrievePublicDatasets} import edu.uci.ics.texera.web.resource.dashboard.user.dataset.`type`.FileNode import edu.uci.ics.texera.web.resource.dashboard.user.dataset.service.GitVersionControlLocalFileStorage import edu.uci.ics.texera.web.resource.dashboard.user.dataset.utils.PathUtils @@ -33,7 +33,7 @@ import scala.jdk.CollectionConverters._ object DatasetResource { val DATASET_IS_PUBLIC: Byte = 1; - + val DATASET_IS_PRIVATE: Byte = 0; val FILE_OPERATION_UPLOAD_PREFIX = "file:upload:" val FILE_OPERATION_REMOVE_PREFIX = "file:remove" @@ -406,6 +406,32 @@ class DatasetResource { } } + @POST + @Path("/{did}/update/publicity") + def toggleDatasetPublicity( + @PathParam("did") did: UInteger, + @Auth sessionUser: SessionUser + ): Response = { + withTransaction(context) { ctx => + val datasetDao = new DatasetDao(ctx.configuration()) + val uid = sessionUser.getUid + + if (!userHasWriteAccess(ctx, did, uid)) { + throw new ForbiddenException(ERR_USER_HAS_NO_ACCESS_TO_DATASET_MESSAGE) + } + + val existedDataset = getDatasetByID(ctx, did, uid) + if (existedDataset.getIsPublic == DATASET_IS_PUBLIC) { + existedDataset.setIsPublic(DATASET_IS_PRIVATE) + } else { + existedDataset.setIsPublic(DATASET_IS_PUBLIC) + } + + datasetDao.update(existedDataset) + Response.ok().build() + } + } + @POST @Path("/{did}/version/create") @Consumes(Array(MediaType.MULTIPART_FORM_DATA)) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/service/ExecutionsMetadataPersistService.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/service/ExecutionsMetadataPersistService.scala index 06ef48ae295..c041604e98b 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/service/ExecutionsMetadataPersistService.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/service/ExecutionsMetadataPersistService.scala @@ -34,7 +34,8 @@ object ExecutionsMetadataPersistService extends LazyLogging { workflowId: WorkflowIdentity, uid: Option[UInteger], executionName: String, - environmentVersion: String + environmentVersion: String, + environmentEid: UInteger ): ExecutionIdentity = { if (!AmberConfig.isUserSystemEnabled) return DEFAULT_EXECUTION_ID // first retrieve the latest version of this workflow @@ -46,7 +47,9 @@ object ExecutionsMetadataPersistService extends LazyLogging { newExecution.setVid(vid) newExecution.setUid(uid.orNull) newExecution.setStartingTime(new Timestamp(System.currentTimeMillis())) + // TODO: consider put environment version as a part of the environment newExecution.setEnvironmentVersion(environmentVersion) + newExecution.setEnvironmentEid(environmentEid) workflowExecutionsDao.insert(newExecution) ExecutionIdentity(newExecution.getEid.longValue()) } diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala index 26499e43d8d..4930aa4e88d 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala @@ -5,7 +5,7 @@ import com.typesafe.scalalogging.LazyLogging import edu.uci.ics.amber.engine.architecture.controller.ControllerConfig import edu.uci.ics.amber.engine.architecture.worker.WorkflowWorker.{FaultToleranceConfig, StateRestoreConfig} import edu.uci.ics.amber.engine.common.AmberConfig -import edu.uci.ics.amber.engine.common.virtualidentity.{ChannelMarkerIdentity, EnvironmentIdentity, ExecutionIdentity, WorkflowIdentity} +import edu.uci.ics.amber.engine.common.virtualidentity.{ChannelMarkerIdentity, ExecutionIdentity, WorkflowIdentity} import edu.uci.ics.texera.web.model.websocket.event.TexeraWebSocketEvent import edu.uci.ics.texera.web.model.websocket.request.WorkflowExecuteRequest import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowResource @@ -146,11 +146,14 @@ class WorkflowService( val workflowContext: WorkflowContext = createWorkflowContext(uidOpt) var controllerConf = ControllerConfig.default + // fetch the workflow's environment eid + val environmentEid = WorkflowResource.getEnvironmentEidOfWorkflow(UInteger.valueOf(workflowContext.workflowId.id)) workflowContext.executionId = ExecutionsMetadataPersistService.insertNewExecution( workflowContext.workflowId, workflowContext.userId, req.executionName, - convertToJson(req.engineVersion) + convertToJson(req.engineVersion), + environmentEid ) if (AmberConfig.isUserSystemEnabled) { diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ActorVirtualIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ActorVirtualIdentity.scala index c41feae4785..c8db20851ef 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ActorVirtualIdentity.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ActorVirtualIdentity.scala @@ -84,8 +84,8 @@ object ActorVirtualIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.ic ) case _ => throw new RuntimeException("Expected PMessage") } - def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(3) - def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(3) + def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(2) + def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(2) def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = throw new MatchError(__number) lazy val nestedMessagesCompanions: Seq[_root_.scalapb.GeneratedMessageCompanion[_ <: _root_.scalapb.GeneratedMessage]] = Seq.empty def enumCompanionForFieldNumber(__fieldNumber: _root_.scala.Int): _root_.scalapb.GeneratedEnumCompanion[_] = throw new MatchError(__fieldNumber) diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelIdentity.scala index 9bc4991c8f5..8297da89498 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelIdentity.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelIdentity.scala @@ -138,8 +138,8 @@ object ChannelIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.ics.amb ) case _ => throw new RuntimeException("Expected PMessage") } - def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(4) - def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(4) + def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(3) + def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(3) def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = { var __out: _root_.scalapb.GeneratedMessageCompanion[_] = null (__number: @_root_.scala.unchecked) match { diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelMarkerIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelMarkerIdentity.scala index a799058f2fc..f9cce189278 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelMarkerIdentity.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/ChannelMarkerIdentity.scala @@ -84,8 +84,8 @@ object ChannelMarkerIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.i ) case _ => throw new RuntimeException("Expected PMessage") } - def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(7) - def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(7) + def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(6) + def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(6) def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = throw new MatchError(__number) lazy val nestedMessagesCompanions: Seq[_root_.scalapb.GeneratedMessageCompanion[_ <: _root_.scalapb.GeneratedMessage]] = Seq.empty def enumCompanionForFieldNumber(__fieldNumber: _root_.scala.Int): _root_.scalapb.GeneratedEnumCompanion[_] = throw new MatchError(__fieldNumber) diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/EnvironmentIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/EnvironmentIdentity.scala deleted file mode 100644 index 6086fad07f9..00000000000 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/EnvironmentIdentity.scala +++ /dev/null @@ -1,105 +0,0 @@ -// Generated by the Scala Plugin for the Protocol Buffer Compiler. -// Do not edit! -// -// Protofile syntax: PROTO3 - -package edu.uci.ics.amber.engine.common.virtualidentity - -@SerialVersionUID(0L) -final case class EnvironmentIdentity( - id: _root_.scala.Long - ) extends scalapb.GeneratedMessage with scalapb.lenses.Updatable[EnvironmentIdentity] { - @transient - private[this] var __serializedSizeCachedValue: _root_.scala.Int = 0 - private[this] def __computeSerializedValue(): _root_.scala.Int = { - var __size = 0 - - { - val __value = id - if (__value != 0L) { - __size += _root_.com.google.protobuf.CodedOutputStream.computeInt64Size(1, __value) - } - }; - __size - } - override def serializedSize: _root_.scala.Int = { - var read = __serializedSizeCachedValue - if (read == 0) { - read = __computeSerializedValue() - __serializedSizeCachedValue = read - } - read - } - def writeTo(`_output__`: _root_.com.google.protobuf.CodedOutputStream): _root_.scala.Unit = { - { - val __v = id - if (__v != 0L) { - _output__.writeInt64(1, __v) - } - }; - } - def withId(__v: _root_.scala.Long): EnvironmentIdentity = copy(id = __v) - def getFieldByNumber(__fieldNumber: _root_.scala.Int): _root_.scala.Any = { - (__fieldNumber: @_root_.scala.unchecked) match { - case 1 => { - val __t = id - if (__t != 0L) __t else null - } - } - } - def getField(__field: _root_.scalapb.descriptors.FieldDescriptor): _root_.scalapb.descriptors.PValue = { - _root_.scala.Predef.require(__field.containingMessage eq companion.scalaDescriptor) - (__field.number: @_root_.scala.unchecked) match { - case 1 => _root_.scalapb.descriptors.PLong(id) - } - } - def toProtoString: _root_.scala.Predef.String = _root_.scalapb.TextFormat.printToSingleLineUnicodeString(this) - def companion = edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity - // @@protoc_insertion_point(GeneratedMessage[edu.uci.ics.amber.engine.common.EnvironmentIdentity]) -} - -object EnvironmentIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity] { - implicit def messageCompanion: scalapb.GeneratedMessageCompanion[edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity] = this - def parseFrom(`_input__`: _root_.com.google.protobuf.CodedInputStream): edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity = { - var __id: _root_.scala.Long = 0L - var _done__ = false - while (!_done__) { - val _tag__ = _input__.readTag() - _tag__ match { - case 0 => _done__ = true - case 8 => - __id = _input__.readInt64() - case tag => _input__.skipField(tag) - } - } - edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity( - id = __id - ) - } - implicit def messageReads: _root_.scalapb.descriptors.Reads[edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity] = _root_.scalapb.descriptors.Reads{ - case _root_.scalapb.descriptors.PMessage(__fieldsMap) => - _root_.scala.Predef.require(__fieldsMap.keys.forall(_.containingMessage eq scalaDescriptor), "FieldDescriptor does not match message type.") - edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity( - id = __fieldsMap.get(scalaDescriptor.findFieldByNumber(1).get).map(_.as[_root_.scala.Long]).getOrElse(0L) - ) - case _ => throw new RuntimeException("Expected PMessage") - } - def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(2) - def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(2) - def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = throw new MatchError(__number) - lazy val nestedMessagesCompanions: Seq[_root_.scalapb.GeneratedMessageCompanion[_ <: _root_.scalapb.GeneratedMessage]] = Seq.empty - def enumCompanionForFieldNumber(__fieldNumber: _root_.scala.Int): _root_.scalapb.GeneratedEnumCompanion[_] = throw new MatchError(__fieldNumber) - lazy val defaultInstance = edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity( - id = 0L - ) - implicit class EnvironmentIdentityLens[UpperPB](_l: _root_.scalapb.lenses.Lens[UpperPB, edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity]) extends _root_.scalapb.lenses.ObjectLens[UpperPB, edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity](_l) { - def id: _root_.scalapb.lenses.Lens[UpperPB, _root_.scala.Long] = field(_.id)((c_, f_) => c_.copy(id = f_)) - } - final val ID_FIELD_NUMBER = 1 - def of( - id: _root_.scala.Long - ): _root_.edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity = _root_.edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity( - id - ) - // @@protoc_insertion_point(GeneratedMessageCompanion[edu.uci.ics.amber.engine.common.EnvironmentIdentity]) -} diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/OperatorIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/OperatorIdentity.scala index 2fa77f74591..b1b79dd7884 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/OperatorIdentity.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/OperatorIdentity.scala @@ -84,8 +84,8 @@ object OperatorIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.ics.am ) case _ => throw new RuntimeException("Expected PMessage") } - def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(5) - def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(5) + def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(4) + def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(4) def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = throw new MatchError(__number) lazy val nestedMessagesCompanions: Seq[_root_.scalapb.GeneratedMessageCompanion[_ <: _root_.scalapb.GeneratedMessage]] = Seq.empty def enumCompanionForFieldNumber(__fieldNumber: _root_.scala.Int): _root_.scalapb.GeneratedEnumCompanion[_] = throw new MatchError(__fieldNumber) diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/PhysicalOpIdentity.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/PhysicalOpIdentity.scala index 233286da588..3075b6e8cda 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/PhysicalOpIdentity.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/PhysicalOpIdentity.scala @@ -111,8 +111,8 @@ object PhysicalOpIdentity extends scalapb.GeneratedMessageCompanion[edu.uci.ics. ) case _ => throw new RuntimeException("Expected PMessage") } - def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(6) - def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(6) + def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = VirtualidentityProto.javaDescriptor.getMessageTypes().get(5) + def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = VirtualidentityProto.scalaDescriptor.messages(5) def messageCompanionForFieldNumber(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[_] = { var __out: _root_.scalapb.GeneratedMessageCompanion[_] = null (__number: @_root_.scala.unchecked) match { diff --git a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/VirtualidentityProto.scala b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/VirtualidentityProto.scala index d211d3d0e03..7f75701f4d7 100644 --- a/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/VirtualidentityProto.scala +++ b/core/amber/src/main/scalapb/edu/uci/ics/amber/engine/common/virtualidentity/VirtualidentityProto.scala @@ -13,7 +13,6 @@ object VirtualidentityProto extends _root_.scalapb.GeneratedFileObject { Seq[_root_.scalapb.GeneratedMessageCompanion[_ <: _root_.scalapb.GeneratedMessage]]( edu.uci.ics.amber.engine.common.virtualidentity.WorkflowIdentity, edu.uci.ics.amber.engine.common.virtualidentity.ExecutionIdentity, - edu.uci.ics.amber.engine.common.virtualidentity.EnvironmentIdentity, edu.uci.ics.amber.engine.common.virtualidentity.ActorVirtualIdentity, edu.uci.ics.amber.engine.common.virtualidentity.ChannelIdentity, edu.uci.ics.amber.engine.common.virtualidentity.OperatorIdentity, @@ -24,16 +23,16 @@ object VirtualidentityProto extends _root_.scalapb.GeneratedFileObject { scalapb.Encoding.fromBase64(scala.collection.immutable.Seq( """CjVlZHUvdWNpL2ljcy9hbWJlci9lbmdpbmUvY29tbW9uL3ZpcnR1YWxpZGVudGl0eS5wcm90bxIfZWR1LnVjaS5pY3MuYW1iZ XIuZW5naW5lLmNvbW1vbhoVc2NhbGFwYi9zY2FsYXBiLnByb3RvIisKEFdvcmtmbG93SWRlbnRpdHkSFwoCaWQYASABKANCB+I/B - BICaWRSAmlkIiwKEUV4ZWN1dGlvbklkZW50aXR5EhcKAmlkGAEgASgDQgfiPwQSAmlkUgJpZCIuChNFbnZpcm9ubWVudElkZW50a - XR5EhcKAmlkGAEgASgDQgfiPwQSAmlkUgJpZCI1ChRBY3RvclZpcnR1YWxJZGVudGl0eRIdCgRuYW1lGAEgASgJQgniPwYSBG5hb - WVSBG5hbWUimwIKD0NoYW5uZWxJZGVudGl0eRJvCgxmcm9tV29ya2VySWQYASABKAsyNS5lZHUudWNpLmljcy5hbWJlci5lbmdpb - mUuY29tbW9uLkFjdG9yVmlydHVhbElkZW50aXR5QhTiPxESDGZyb21Xb3JrZXJJZPABAVIMZnJvbVdvcmtlcklkEmkKCnRvV29ya - 2VySWQYAiABKAsyNS5lZHUudWNpLmljcy5hbWJlci5lbmdpbmUuY29tbW9uLkFjdG9yVmlydHVhbElkZW50aXR5QhLiPw8SCnRvV - 29ya2VySWTwAQFSCnRvV29ya2VySWQSLAoJaXNDb250cm9sGAMgASgIQg7iPwsSCWlzQ29udHJvbFIJaXNDb250cm9sIisKEE9wZ - XJhdG9ySWRlbnRpdHkSFwoCaWQYASABKAlCB+I/BBICaWRSAmlkIqwBChJQaHlzaWNhbE9wSWRlbnRpdHkSaAoLbG9naWNhbE9wS - WQYASABKAsyMS5lZHUudWNpLmljcy5hbWJlci5lbmdpbmUuY29tbW9uLk9wZXJhdG9ySWRlbnRpdHlCE+I/EBILbG9naWNhbE9wS - WTwAQFSC2xvZ2ljYWxPcElkEiwKCWxheWVyTmFtZRgCIAEoCUIO4j8LEglsYXllck5hbWVSCWxheWVyTmFtZSIwChVDaGFubmVsT - WFya2VySWRlbnRpdHkSFwoCaWQYASABKAlCB+I/BBICaWRSAmlkQgniPwZIAFgAeAFiBnByb3RvMw==""" + BICaWRSAmlkIiwKEUV4ZWN1dGlvbklkZW50aXR5EhcKAmlkGAEgASgDQgfiPwQSAmlkUgJpZCI1ChRBY3RvclZpcnR1YWxJZGVud + Gl0eRIdCgRuYW1lGAEgASgJQgniPwYSBG5hbWVSBG5hbWUimwIKD0NoYW5uZWxJZGVudGl0eRJvCgxmcm9tV29ya2VySWQYASABK + AsyNS5lZHUudWNpLmljcy5hbWJlci5lbmdpbmUuY29tbW9uLkFjdG9yVmlydHVhbElkZW50aXR5QhTiPxESDGZyb21Xb3JrZXJJZ + PABAVIMZnJvbVdvcmtlcklkEmkKCnRvV29ya2VySWQYAiABKAsyNS5lZHUudWNpLmljcy5hbWJlci5lbmdpbmUuY29tbW9uLkFjd + G9yVmlydHVhbElkZW50aXR5QhLiPw8SCnRvV29ya2VySWTwAQFSCnRvV29ya2VySWQSLAoJaXNDb250cm9sGAMgASgIQg7iPwsSC + WlzQ29udHJvbFIJaXNDb250cm9sIisKEE9wZXJhdG9ySWRlbnRpdHkSFwoCaWQYASABKAlCB+I/BBICaWRSAmlkIqwBChJQaHlza + WNhbE9wSWRlbnRpdHkSaAoLbG9naWNhbE9wSWQYASABKAsyMS5lZHUudWNpLmljcy5hbWJlci5lbmdpbmUuY29tbW9uLk9wZXJhd + G9ySWRlbnRpdHlCE+I/EBILbG9naWNhbE9wSWTwAQFSC2xvZ2ljYWxPcElkEiwKCWxheWVyTmFtZRgCIAEoCUIO4j8LEglsYXllc + k5hbWVSCWxheWVyTmFtZSIwChVDaGFubmVsTWFya2VySWRlbnRpdHkSFwoCaWQYASABKAlCB+I/BBICaWRSAmlkQgniPwZIAFgAe + AFiBnByb3RvMw==""" ).mkString) lazy val scalaDescriptor: _root_.scalapb.descriptors.FileDescriptor = { val scalaProto = com.google.protobuf.descriptor.FileDescriptorProto.parseFrom(ProtoBytes) diff --git a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.html b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.html index 5f6ca75ef76..212a595cf2d 100644 --- a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.html +++ b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.html @@ -7,6 +7,14 @@

Dataset: {{datasetName}}

+
+ +
diff --git a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.ts b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.ts index a2861ec5b25..e989cdc33d6 100644 --- a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.ts +++ b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.ts @@ -28,6 +28,7 @@ export class UserDatasetExplorerComponent implements OnInit { public datasetName: string = ""; public datasetDescription: string = ""; public datasetCreationTime: string = ""; + public datasetIsPublic: boolean = false; public userDatasetAccessLevel: "READ" | "WRITE" | "NONE" = "NONE"; public currentDisplayedFileName: string = ""; @@ -131,6 +132,27 @@ export class UserDatasetExplorerComponent implements OnInit { this.renderVersionCreatorSider(); } + onPublicStatusChange(checked: boolean): void { + // Handle the change in dataset public status + if (this.did) { + this.datasetService.updateDatasetPublicity(this.did) + .pipe(untilDestroyed(this)) + .subscribe({ + next: res => { + this.datasetIsPublic = checked; + let state = "public"; + if (!this.datasetIsPublic) { + state = "private"; + } + this.notificationService.success(`Dataset ${this.datasetName} is now ${state}`); + }, + error: err => { + this.notificationService.error(`Fail to change the dataset publicity`); + } + }) + } + } + retrieveDatasetInfo() { if (this.did) { this.datasetService @@ -141,6 +163,7 @@ export class UserDatasetExplorerComponent implements OnInit { this.datasetName = dataset.name; this.datasetDescription = dataset.description; this.userDatasetAccessLevel = dashboardDataset.accessPrivilege; + this.datasetIsPublic = dataset.isPublic === 1; if (typeof dataset.creationTime === "number") { this.datasetCreationTime = new Date(dataset.creationTime).toString(); } diff --git a/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts b/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts index d215fb8f40c..5cd6e8676f3 100644 --- a/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts +++ b/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts @@ -21,6 +21,7 @@ export const DATASET_CREATE_URL = DATASET_BASE_URL + "/create"; export const DATASET_UPDATE_BASE_URL = DATASET_BASE_URL + "/update"; export const DATASET_UPDATE_NAME_URL = DATASET_UPDATE_BASE_URL + "/name"; export const DATASET_UPDATE_DESCRIPTION_URL = DATASET_UPDATE_BASE_URL + "/description"; +export const DATASET_UPDATE_PUBLICITY_URL = "update/publicity" export const DATASET_LIST_URL = DATASET_BASE_URL + "/list"; export const DATASET_SEARCH_URL = DATASET_BASE_URL + "/search"; export const DATASET_DELETE_URL = DATASET_BASE_URL + "/delete"; @@ -171,4 +172,8 @@ export class DatasetService { description: description, }); } + + public updateDatasetPublicity(did: number): Observable { + return this.http.post(`${AppSettings.getApiEndpoint()}/${DATASET_BASE_URL}/${did}/${DATASET_UPDATE_PUBLICITY_URL}`, {}) + }; } diff --git a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts index 610223c6b9c..6b99b784450 100644 --- a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts +++ b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts @@ -130,7 +130,6 @@ export class EnvironmentComponent implements OnInit { // initialize the datasets info this.datasetService.retrieveAccessibleDatasets().subscribe({ next: datasets => { - console.log(datasets); this.userAccessibleDatasets = datasets.filter(ds => { const newDid = ds.dataset.did; const newName = ds.dataset.name; From 63d62d46d8269605f3337644d63e1ef8249430a6 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Mon, 4 Mar 2024 23:44:17 -0800 Subject: [PATCH 07/10] fmt --- .../dashboard/DatasetSearchQueryBuilder.scala | 2 +- .../user/dataset/DatasetResource.scala | 62 ++++++++-- .../user/dataset/type/DatasetFileDesc.scala | 11 +- .../environment/EnvironmentResource.scala | 111 ++++++++++++------ .../user/workflow/WorkflowResource.scala | 13 +- .../texera/web/service/WorkflowService.scala | 14 ++- .../source/scan/ScanSourceOpDesc.scala | 7 +- core/new-gui/src/app/app.module.ts | 2 +- .../workflow-persist.service.ts | 2 +- .../src/app/common/type/environment.ts | 2 +- .../user-dataset-explorer.component.html | 2 +- .../user-dataset-explorer.component.ts | 48 ++++---- .../service/user-dataset/dataset.service.ts | 9 +- .../user-environment/environment.service.ts | 10 +- .../input-autocomplete.component.ts | 9 +- .../environment/environment.component.ts | 11 +- .../left-panel/left-panel.component.ts | 6 +- 17 files changed, 215 insertions(+), 106 deletions(-) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala index 19140ef40f4..66a18f6d0d8 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala @@ -39,7 +39,7 @@ object DatasetSearchQueryBuilder extends SearchQueryBuilder { .leftJoin(DATASET_USER_ACCESS) .on(DATASET_USER_ACCESS.DID.eq(DATASET.DID)) .where( - DATASET_USER_ACCESS.UID.eq(uid) + DATASET_USER_ACCESS.UID.eq(uid) ) } diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala index bb1ef5640e2..edacfb333ed 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala @@ -4,14 +4,46 @@ import edu.uci.ics.texera.Utils.withTransaction import edu.uci.ics.texera.web.SqlServer import edu.uci.ics.texera.web.auth.SessionUser import edu.uci.ics.texera.web.model.jooq.generated.enums.DatasetUserAccessPrivilege -import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{DatasetDao, DatasetUserAccessDao, DatasetVersionDao} -import edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.{Dataset, DatasetUserAccess, DatasetVersion} +import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{ + DatasetDao, + DatasetUserAccessDao, + DatasetVersionDao +} +import edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.{ + Dataset, + DatasetUserAccess, + DatasetVersion +} import edu.uci.ics.texera.web.model.jooq.generated.tables.Dataset.DATASET import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetVersion.DATASET_VERSION import edu.uci.ics.texera.web.resource.dashboard.DashboardResource import edu.uci.ics.texera.web.resource.dashboard.DashboardResource.SearchQueryParams -import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetAccessResource.{getDatasetUserAccessPrivilege, userHasReadAccess, userHasWriteAccess, userOwnDataset} -import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetResource.{DATASET_IS_PRIVATE, DATASET_IS_PUBLIC, DashboardDataset, DashboardDatasetVersion, DatasetDescriptionModification, DatasetIDs, DatasetNameModification, DatasetVersionRootFileNodes, DatasetVersions, ERR_DATASET_CREATION_FAILED_MESSAGE, ERR_USER_HAS_NO_ACCESS_TO_DATASET_MESSAGE, context, createNewDatasetVersion, getDashboardDataset, getDatasetByID, getDatasetLatestVersion, getDatasetVersionHashByID, retrievePublicDatasets} +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetAccessResource.{ + getDatasetUserAccessPrivilege, + userHasReadAccess, + userHasWriteAccess, + userOwnDataset +} +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetResource.{ + DATASET_IS_PRIVATE, + DATASET_IS_PUBLIC, + DashboardDataset, + DashboardDatasetVersion, + DatasetDescriptionModification, + DatasetIDs, + DatasetNameModification, + DatasetVersionRootFileNodes, + DatasetVersions, + ERR_DATASET_CREATION_FAILED_MESSAGE, + ERR_USER_HAS_NO_ACCESS_TO_DATASET_MESSAGE, + context, + createNewDatasetVersion, + getDashboardDataset, + getDatasetByID, + getDatasetLatestVersion, + getDatasetVersionHashByID, + retrievePublicDatasets +} import edu.uci.ics.texera.web.resource.dashboard.user.dataset.`type`.FileNode import edu.uci.ics.texera.web.resource.dashboard.user.dataset.service.GitVersionControlLocalFileStorage import edu.uci.ics.texera.web.resource.dashboard.user.dataset.utils.PathUtils @@ -27,7 +59,18 @@ import java.nio.file.Paths import java.util import java.util.concurrent.locks.ReentrantLock import javax.annotation.security.RolesAllowed -import javax.ws.rs.{BadRequestException, Consumes, ForbiddenException, GET, NotFoundException, POST, Path, PathParam, Produces, QueryParam} +import javax.ws.rs.{ + BadRequestException, + Consumes, + ForbiddenException, + GET, + NotFoundException, + POST, + Path, + PathParam, + Produces, + QueryParam +} import javax.ws.rs.core.{MediaType, Response, StreamingOutput} import scala.jdk.CollectionConverters._ @@ -233,8 +276,7 @@ object DatasetResource { FileNode.getAllFileRelativePaths(fileNodes) } - def retrievePublicDatasets( - ctx: DSLContext): util.List[Dataset] = { + def retrievePublicDatasets(ctx: DSLContext): util.List[Dataset] = { val datasetDao = new DatasetDao(ctx.configuration()) datasetDao.fetchByIsPublic(DATASET_IS_PUBLIC) } @@ -409,9 +451,9 @@ class DatasetResource { @POST @Path("/{did}/update/publicity") def toggleDatasetPublicity( - @PathParam("did") did: UInteger, - @Auth sessionUser: SessionUser - ): Response = { + @PathParam("did") did: UInteger, + @Auth sessionUser: SessionUser + ): Response = { withTransaction(context) { ctx => val datasetDao = new DatasetDao(ctx.configuration()) val uid = sessionUser.getUid diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/DatasetFileDesc.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/DatasetFileDesc.scala index 4da7ed632e0..02dd74360cc 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/DatasetFileDesc.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/DatasetFileDesc.scala @@ -6,8 +6,13 @@ import java.nio.file.Path // This file class DatasetFileDesc(val fileName: Path, val datasetPath: Path, val versionHash: String) { def tempFilePath(): Path = { - GitVersionControlLocalFileStorage.writeVersionedFileToTempFile(datasetPath, versionHash, datasetPath.resolve(fileName)) + GitVersionControlLocalFileStorage.writeVersionedFileToTempFile( + datasetPath, + versionHash, + datasetPath.resolve(fileName) + ) } - override def toString: String = s"DatasetFileDesc(fileName=$fileName, datasetPath=$datasetPath, versionHash=$versionHash)" -} \ No newline at end of file + override def toString: String = + s"DatasetFileDesc(fileName=$fileName, datasetPath=$datasetPath, versionHash=$versionHash)" +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/environment/EnvironmentResource.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/environment/EnvironmentResource.scala index 021bf6313ea..632cf5d19ff 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/environment/EnvironmentResource.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/environment/EnvironmentResource.scala @@ -3,15 +3,46 @@ package edu.uci.ics.texera.web.resource.dashboard.user.environment import edu.uci.ics.texera.Utils.withTransaction import edu.uci.ics.texera.web.SqlServer import edu.uci.ics.texera.web.auth.SessionUser -import edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.{Dataset, DatasetOfEnvironment, DatasetVersion, Environment} +import edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.{ + Dataset, + DatasetOfEnvironment, + DatasetVersion, + Environment +} import edu.uci.ics.texera.web.model.jooq.generated.tables.Environment.ENVIRONMENT import edu.uci.ics.texera.web.model.jooq.generated.tables.EnvironmentOfWorkflow.ENVIRONMENT_OF_WORKFLOW import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetOfEnvironment.DATASET_OF_ENVIRONMENT -import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{DatasetDao, DatasetOfEnvironmentDao, DatasetVersionDao, EnvironmentDao, EnvironmentOfWorkflowDao} +import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{ + DatasetDao, + DatasetOfEnvironmentDao, + DatasetVersionDao, + EnvironmentDao, + EnvironmentOfWorkflowDao +} import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetResource.retrieveDatasetVersionFilePaths import edu.uci.ics.texera.web.resource.dashboard.user.dataset.`type`.DatasetFileDesc -import edu.uci.ics.texera.web.resource.dashboard.user.dataset.{DatasetAccessResource, DatasetResource} -import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource.{DashboardEnvironment, DatasetID, DatasetOfEnvironmentAlreadyExistsMessage, DatasetOfEnvironmentDetails, DatasetOfEnvironmentDoseNotExistMessage, EnvironmentIDs, UserNoPermissionExceptionMessage, WorkflowLink, context, doesDatasetExistInEnvironment, doesUserOwnEnvironment, getEnvironmentByEid, retrieveDatasetsAndVersions, retrieveDatasetsOfEnvironmentFileList, userHasReadAccessToEnvironment, userHasWriteAccessToEnvironment} +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.{ + DatasetAccessResource, + DatasetResource +} +import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource.{ + DashboardEnvironment, + DatasetID, + DatasetOfEnvironmentAlreadyExistsMessage, + DatasetOfEnvironmentDetails, + DatasetOfEnvironmentDoseNotExistMessage, + EnvironmentIDs, + UserNoPermissionExceptionMessage, + WorkflowLink, + context, + doesDatasetExistInEnvironment, + doesUserOwnEnvironment, + getEnvironmentByEid, + retrieveDatasetsAndVersions, + retrieveDatasetsOfEnvironmentFileList, + userHasReadAccessToEnvironment, + userHasWriteAccessToEnvironment +} import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowAccessResource import io.dropwizard.auth.Auth import org.jooq.DSLContext @@ -68,41 +99,55 @@ object EnvironmentResource { // return the descriptor of the target file. // The filename is passed from the frontend, the did is contained in the filename in the format of /{dataset-name}/{filepath} - def getEnvironmentDatasetFilePathAndVersion(uid: UInteger, eid: UInteger, fileName: String): DatasetFileDesc = { - withTransaction(context) { ctx => { - // Adjust the pattern to match the new fileName format - val datasetNamePattern: Regex = """/([^/]+)/.*""".r - - // Extract 'datasetName' using the pattern - val datasetName = datasetNamePattern.findFirstMatchIn(fileName) match { - case Some(matched) => matched.group(1) // Extract the first group which is 'datasetName' - case None => throw new RuntimeException("The fileName format is not correct") // Default value or handle error - } - - // Extract the file path - val filePath = Paths.get(fileName.substring(fileName.indexOf(s"/$datasetName/") + s"/$datasetName/".length)) - val datasetsOfEnvironment = retrieveDatasetsAndVersions(ctx, uid, eid) - - // Initialize datasetFileDesc as None - var datasetFileDesc: Option[DatasetFileDesc] = None + def getEnvironmentDatasetFilePathAndVersion( + uid: UInteger, + eid: UInteger, + fileName: String + ): DatasetFileDesc = { + withTransaction(context) { ctx => + { + // Adjust the pattern to match the new fileName format + val datasetNamePattern: Regex = """/([^/]+)/.*""".r + + // Extract 'datasetName' using the pattern + val datasetName = datasetNamePattern.findFirstMatchIn(fileName) match { + case Some(matched) => matched.group(1) // Extract the first group which is 'datasetName' + case None => + throw new RuntimeException( + "The fileName format is not correct" + ) // Default value or handle error + } - // Iterate over datasetsOfEnvironment to find a match based on datasetName - datasetsOfEnvironment.foreach { datasetAndVersion => - if (datasetAndVersion.dataset.getName == datasetName) { - datasetFileDesc = Some(new DatasetFileDesc( - filePath, - Paths.get(datasetAndVersion.dataset.getStoragePath), - datasetAndVersion.version.getVersionHash)) + // Extract the file path + val filePath = Paths.get( + fileName.substring(fileName.indexOf(s"/$datasetName/") + s"/$datasetName/".length) + ) + val datasetsOfEnvironment = retrieveDatasetsAndVersions(ctx, uid, eid) + + // Initialize datasetFileDesc as None + var datasetFileDesc: Option[DatasetFileDesc] = None + + // Iterate over datasetsOfEnvironment to find a match based on datasetName + datasetsOfEnvironment.foreach { datasetAndVersion => + if (datasetAndVersion.dataset.getName == datasetName) { + datasetFileDesc = Some( + new DatasetFileDesc( + filePath, + Paths.get(datasetAndVersion.dataset.getStoragePath), + datasetAndVersion.version.getVersionHash + ) + ) + } } - } - // Check if datasetFileDesc is set, if not, throw an exception - datasetFileDesc.getOrElse(throw new RuntimeException("Given file is not found in the environment")) - } + // Check if datasetFileDesc is set, if not, throw an exception + datasetFileDesc.getOrElse( + throw new RuntimeException("Given file is not found in the environment") + ) + } } } - private def getEnvironmentByEid(ctx: DSLContext, eid: UInteger): Environment = { val environmentDao: EnvironmentDao = new EnvironmentDao(ctx.configuration()) val env = environmentDao.fetchOneByEid(eid) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala index 958409cf537..9396c4fc250 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala @@ -5,10 +5,19 @@ import edu.uci.ics.texera.web.SqlServer import edu.uci.ics.texera.web.auth.SessionUser import edu.uci.ics.texera.web.model.jooq.generated.Tables._ import edu.uci.ics.texera.web.model.jooq.generated.enums.WorkflowUserAccessPrivilege -import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{EnvironmentOfWorkflowDao, WorkflowDao, WorkflowOfProjectDao, WorkflowOfUserDao, WorkflowUserAccessDao} +import edu.uci.ics.texera.web.model.jooq.generated.tables.daos.{ + EnvironmentOfWorkflowDao, + WorkflowDao, + WorkflowOfProjectDao, + WorkflowOfUserDao, + WorkflowUserAccessDao +} import edu.uci.ics.texera.web.model.jooq.generated.tables.pojos._ import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource -import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource.{createEnvironment, doesWorkflowHaveEnvironment} +import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource.{ + createEnvironment, + doesWorkflowHaveEnvironment +} import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowAccessResource.hasReadAccess import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowResource._ import io.dropwizard.auth.Auth diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala index 4930aa4e88d..ccabc880eea 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/service/WorkflowService.scala @@ -3,9 +3,16 @@ package edu.uci.ics.texera.web.service import com.google.protobuf.timestamp.Timestamp import com.typesafe.scalalogging.LazyLogging import edu.uci.ics.amber.engine.architecture.controller.ControllerConfig -import edu.uci.ics.amber.engine.architecture.worker.WorkflowWorker.{FaultToleranceConfig, StateRestoreConfig} +import edu.uci.ics.amber.engine.architecture.worker.WorkflowWorker.{ + FaultToleranceConfig, + StateRestoreConfig +} import edu.uci.ics.amber.engine.common.AmberConfig -import edu.uci.ics.amber.engine.common.virtualidentity.{ChannelMarkerIdentity, ExecutionIdentity, WorkflowIdentity} +import edu.uci.ics.amber.engine.common.virtualidentity.{ + ChannelMarkerIdentity, + ExecutionIdentity, + WorkflowIdentity +} import edu.uci.ics.texera.web.model.websocket.event.TexeraWebSocketEvent import edu.uci.ics.texera.web.model.websocket.request.WorkflowExecuteRequest import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowResource @@ -147,7 +154,8 @@ class WorkflowService( var controllerConf = ControllerConfig.default // fetch the workflow's environment eid - val environmentEid = WorkflowResource.getEnvironmentEidOfWorkflow(UInteger.valueOf(workflowContext.workflowId.id)) + val environmentEid = + WorkflowResource.getEnvironmentEidOfWorkflow(UInteger.valueOf(workflowContext.workflowId.id)) workflowContext.executionId = ExecutionsMetadataPersistService.insertNewExecution( workflowContext.workflowId, workflowContext.userId, diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala b/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala index f16d36b0440..d7c731ab341 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala @@ -68,8 +68,11 @@ abstract class ScanSourceOpDesc extends SourceOperatorDescriptor { // /datasetName/fileName // resolve fileName to be the actual file path. // fetch the environment id that workflow is in - val environmentEid = WorkflowResource.getEnvironmentEidOfWorkflow(UInteger.valueOf(workflowContext.workflowId.id)) - val datasetFileDescriptor = getEnvironmentDatasetFilePathAndVersion(getContext.userId.get, environmentEid, fileName.get) + val environmentEid = WorkflowResource.getEnvironmentEidOfWorkflow( + UInteger.valueOf(workflowContext.workflowId.id) + ) + val datasetFileDescriptor = + getEnvironmentDatasetFilePathAndVersion(getContext.userId.get, environmentEid, fileName.get) filePath = Some(datasetFileDescriptor.tempFilePath().toString) } else { // otherwise, the fileName will be inputted by user, which is the filePath. diff --git a/core/new-gui/src/app/app.module.ts b/core/new-gui/src/app/app.module.ts index cdccb977abd..5785bc8a23c 100644 --- a/core/new-gui/src/app/app.module.ts +++ b/core/new-gui/src/app/app.module.ts @@ -134,7 +134,7 @@ import { NgxFileDropModule } from "ngx-file-drop"; 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 {EnvironmentComponent} from "./workspace/component/left-panel/environment/environment.component"; +import { EnvironmentComponent } from "./workspace/component/left-panel/environment/environment.component"; registerLocaleData(en); diff --git a/core/new-gui/src/app/common/service/workflow-persist/workflow-persist.service.ts b/core/new-gui/src/app/common/service/workflow-persist/workflow-persist.service.ts index 53234e2bf30..0b9ab355cd7 100644 --- a/core/new-gui/src/app/common/service/workflow-persist/workflow-persist.service.ts +++ b/core/new-gui/src/app/common/service/workflow-persist/workflow-persist.service.ts @@ -8,7 +8,7 @@ import { DashboardWorkflow } from "../../../dashboard/user/type/dashboard-workfl import { WorkflowUtilService } from "../../../workspace/service/workflow-graph/util/workflow-util.service"; import { NotificationService } from "../notification/notification.service"; import { SearchFilterParameters, toQueryStrings } from "src/app/dashboard/user/type/search-filter-parameters"; -import {Environment} from "../../type/environment"; +import { Environment } from "../../type/environment"; export const WORKFLOW_BASE_URL = "workflow"; export const WORKFLOW_PERSIST_URL = WORKFLOW_BASE_URL + "/persist"; diff --git a/core/new-gui/src/app/common/type/environment.ts b/core/new-gui/src/app/common/type/environment.ts index 9228f86ecf5..f4236ae0fde 100644 --- a/core/new-gui/src/app/common/type/environment.ts +++ b/core/new-gui/src/app/common/type/environment.ts @@ -1,4 +1,4 @@ -import {Dataset, DatasetVersion} from "./dataset"; +import { Dataset, DatasetVersion } from "./dataset"; export interface Environment { eid: number | undefined; diff --git a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.html b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.html index 212a595cf2d..3955e6c0e5d 100644 --- a/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.html +++ b/core/new-gui/src/app/dashboard/user/component/user-dataset/user-dataset-explorer/user-dataset-explorer.component.html @@ -7,7 +7,7 @@

Dataset: {{datasetName}}

-
+
{ - this.datasetIsPublic = checked; - let state = "public"; - if (!this.datasetIsPublic) { - state = "private"; - } - this.notificationService.success(`Dataset ${this.datasetName} is now ${state}`); - }, - error: err => { - this.notificationService.error(`Fail to change the dataset publicity`); - } - }) - } + // Handle the change in dataset public status + if (this.did) { + this.datasetService + .updateDatasetPublicity(this.did) + .pipe(untilDestroyed(this)) + .subscribe({ + next: (res: Response) => { + this.datasetIsPublic = checked; + let state = "public"; + if (!this.datasetIsPublic) { + state = "private"; + } + this.notificationService.success(`Dataset ${this.datasetName} is now ${state}`); + }, + error: (err: unknown) => { + this.notificationService.error("Fail to change the dataset publicity"); + }, + }); + } } retrieveDatasetInfo() { diff --git a/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts b/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts index 5cd6e8676f3..bbe2dfe8245 100644 --- a/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts +++ b/core/new-gui/src/app/dashboard/user/service/user-dataset/dataset.service.ts @@ -21,7 +21,7 @@ export const DATASET_CREATE_URL = DATASET_BASE_URL + "/create"; export const DATASET_UPDATE_BASE_URL = DATASET_BASE_URL + "/update"; export const DATASET_UPDATE_NAME_URL = DATASET_UPDATE_BASE_URL + "/name"; export const DATASET_UPDATE_DESCRIPTION_URL = DATASET_UPDATE_BASE_URL + "/description"; -export const DATASET_UPDATE_PUBLICITY_URL = "update/publicity" +export const DATASET_UPDATE_PUBLICITY_URL = "update/publicity"; export const DATASET_LIST_URL = DATASET_BASE_URL + "/list"; export const DATASET_SEARCH_URL = DATASET_BASE_URL + "/search"; export const DATASET_DELETE_URL = DATASET_BASE_URL + "/delete"; @@ -174,6 +174,9 @@ export class DatasetService { } public updateDatasetPublicity(did: number): Observable { - return this.http.post(`${AppSettings.getApiEndpoint()}/${DATASET_BASE_URL}/${did}/${DATASET_UPDATE_PUBLICITY_URL}`, {}) - }; + return this.http.post( + `${AppSettings.getApiEndpoint()}/${DATASET_BASE_URL}/${did}/${DATASET_UPDATE_PUBLICITY_URL}`, + {} + ); + } } diff --git a/core/new-gui/src/app/dashboard/user/service/user-environment/environment.service.ts b/core/new-gui/src/app/dashboard/user/service/user-environment/environment.service.ts index 09231a595be..41e56d7d715 100644 --- a/core/new-gui/src/app/dashboard/user/service/user-environment/environment.service.ts +++ b/core/new-gui/src/app/dashboard/user/service/user-environment/environment.service.ts @@ -5,9 +5,9 @@ import next from "ajv/dist/vocabularies/next"; import { Observable, of, throwError } from "rxjs"; import { catchError, filter, map } from "rxjs/operators"; import { AppSettings } from "../../../../common/app-setting"; -import {DatasetOfEnvironment, DatasetOfEnvironmentDetails, Environment} from "../../../../common/type/environment"; -import {DashboardDataset} from "../../type/dashboard-dataset.interface"; -import {DATASET_BASE_URL} from "../user-dataset/dataset.service"; +import { DatasetOfEnvironment, DatasetOfEnvironmentDetails, Environment } from "../../../../common/type/environment"; +import { DashboardDataset } from "../../type/dashboard-dataset.interface"; +import { DATASET_BASE_URL } from "../user-dataset/dataset.service"; export const ENVIRONMENT_BASE_URL = "environment"; export const ENVIRONMENT_CREATE_URL = ENVIRONMENT_BASE_URL + "/create"; @@ -24,9 +24,7 @@ export const ENVIRONMENT_DATASET_REMOVE_URL = ENVIRONMENT_DATASET_RETRIEVAL_URL providedIn: "root", }) export class EnvironmentService { - constructor( - private http: HttpClient, - ) {} + constructor(private http: HttpClient) {} addDatasetToEnvironment(eid: number, did: number): Observable { return this.http.post( diff --git a/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts b/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts index 089c7a525f0..69f5cdaf1a1 100644 --- a/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts +++ b/core/new-gui/src/app/workspace/component/input-autocomplete/input-autocomplete.component.ts @@ -3,9 +3,9 @@ import { FieldType, FieldTypeConfig } from "@ngx-formly/core"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; import { debounceTime } from "rxjs/operators"; import { map } from "rxjs"; -import {WorkflowActionService} from "../../service/workflow-graph/model/workflow-action.service"; -import {EnvironmentService} from "../../../dashboard/user/service/user-environment/environment.service"; -import {WorkflowPersistService} from "../../../common/service/workflow-persist/workflow-persist.service"; +import { WorkflowActionService } from "../../service/workflow-graph/model/workflow-action.service"; +import { EnvironmentService } from "../../../dashboard/user/service/user-environment/environment.service"; +import { WorkflowPersistService } from "../../../common/service/workflow-persist/workflow-persist.service"; @UntilDestroy() @Component({ @@ -59,4 +59,5 @@ export class InputAutoCompleteComponent extends FieldType { }, }); } - }} + } +} diff --git a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts index 6b99b784450..7fd3b249e16 100644 --- a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts +++ b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts @@ -10,7 +10,7 @@ import { } from "../../../../common/type/datasetVersionFileTree"; import { DatasetService } from "../../../../dashboard/user/service/user-dataset/dataset.service"; import { DashboardDataset } from "../../../../dashboard/user/type/dashboard-dataset.interface"; -import {DatasetOfEnvironmentDetails, Environment} from "../../../../common/type/environment"; +import { DatasetOfEnvironmentDetails, Environment } from "../../../../common/type/environment"; @UntilDestroy() @Component({ @@ -59,11 +59,11 @@ export class EnvironmentComponent implements OnInit { private notificationService: NotificationService, private workflowPersistService: WorkflowPersistService, private workflowActionService: WorkflowActionService, - private datasetService: DatasetService, + private datasetService: DatasetService ) {} ngOnInit(): void { - // initialize the environment info + // initialize the environment info this.wid = this.workflowActionService.getWorkflowMetadata()?.wid; if (this.wid) { // use wid to fetch the eid first @@ -138,7 +138,9 @@ export class EnvironmentComponent implements OnInit { const didNotExist = newDid && !this.datasetsOfEnvironment.has(newDid); // Check if the datasetsOfEnvironment does not have the newName - const nameNotExist = ![...this.datasetsOfEnvironment.values()].some(([details, _]) => details.dataset.name === newName); + const nameNotExist = ![...this.datasetsOfEnvironment.values()].some( + ([details, _]) => details.dataset.name === newName + ); return didNotExist && nameNotExist; }); @@ -189,7 +191,6 @@ export class EnvironmentComponent implements OnInit { // console.log(this.filteredLinkingDatasetsName) } - // controls of dataset details get showingDatasetName(): string { if (this.showingDataset?.dataset.name) { diff --git a/core/new-gui/src/app/workspace/component/left-panel/left-panel.component.ts b/core/new-gui/src/app/workspace/component/left-panel/left-panel.component.ts index 1c2d9f6eafb..f98af64b0d9 100644 --- a/core/new-gui/src/app/workspace/component/left-panel/left-panel.component.ts +++ b/core/new-gui/src/app/workspace/component/left-panel/left-panel.component.ts @@ -6,7 +6,7 @@ import { environment } from "../../../../environments/environment"; import { OperatorMenuComponent } from "./operator-menu/operator-menu.component"; import { VersionsListComponent } from "./versions-list/versions-list.component"; import { TimeTravelComponent } from "./time-travel/time-travel.component"; -import {EnvironmentComponent} from "./environment/environment.component"; +import { EnvironmentComponent } from "./environment/environment.component"; @UntilDestroy() @Component({ @@ -35,8 +35,8 @@ export class LeftPanelComponent implements OnDestroy, OnInit { component: EnvironmentComponent, title: "Environment", icon: "dashboard", - enabled: environment.userSystemEnabled - } + enabled: environment.userSystemEnabled, + }, ]; order = [1, 2, 3, 4]; From 0ba97cc17ccae87a78296b5c4300f75fe12a80bc Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Tue, 5 Mar 2024 08:24:49 -0800 Subject: [PATCH 08/10] fmt --- .../dashboard/DatasetSearchQueryBuilder.scala | 1 - .../user/workflow/WorkflowResource.scala | 2 +- .../source/scan/ScanSourceOpDesc.scala | 1 - .../environment/environment.component.ts | 19 +++++++++++++++---- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala index 66a18f6d0d8..9e6f2a46571 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/DatasetSearchQueryBuilder.scala @@ -6,7 +6,6 @@ import edu.uci.ics.texera.web.model.jooq.generated.Tables.{DATASET, DATASET_USER import edu.uci.ics.texera.web.model.jooq.generated.enums.DatasetUserAccessPrivilege import edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.Dataset import edu.uci.ics.texera.web.resource.dashboard.DashboardResource.DashboardClickableFileEntry -import edu.uci.ics.texera.web.resource.dashboard.user.dataset.DatasetResource import edu.uci.ics.texera.web.resource.dashboard.FulltextSearchQueryUtils.{ getContainsFilter, getDateFilter, diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala index 9396c4fc250..9c10d0e4d99 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala @@ -21,7 +21,7 @@ import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentRes import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowAccessResource.hasReadAccess import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowResource._ import io.dropwizard.auth.Auth -import org.jooq.{Condition, DSLContext} +import org.jooq.Condition import org.jooq.impl.DSL.{groupConcatDistinct, noCondition} import org.jooq.types.UInteger diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala b/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala index d7c731ab341..5aea0151f43 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/workflow/operators/source/scan/ScanSourceOpDesc.scala @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle import edu.uci.ics.amber.engine.common.workflow.OutputPort import edu.uci.ics.texera.web.resource.dashboard.user.environment.EnvironmentResource.getEnvironmentDatasetFilePathAndVersion -import edu.uci.ics.texera.web.resource.dashboard.user.file.UserFileAccessResource import edu.uci.ics.texera.web.resource.dashboard.user.workflow.WorkflowResource import edu.uci.ics.texera.workflow.common.WorkflowContext import edu.uci.ics.texera.workflow.common.metadata.{OperatorGroupConstants, OperatorInfo} diff --git a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts index 7fd3b249e16..3d9e33078af 100644 --- a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts +++ b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts @@ -95,13 +95,19 @@ export class EnvironmentComponent implements OnInit { this.datasetFileTrees = []; if (this.eid) { const eid = this.eid; - this.environmentService.retrieveDatasetsOfEnvironmentDetails(eid).subscribe({ + this.environmentService + .retrieveDatasetsOfEnvironmentDetails(eid) + .pipe(untilDestroyed(this)) + .subscribe({ next: datasets => { datasets.forEach(entry => { const did = entry.dataset.did; const dvid = entry.version.dvid; if (did && dvid) { - this.datasetService.retrieveDatasetVersionFileTree(did, dvid).subscribe({ + this.datasetService + .retrieveDatasetVersionFileTree(did, dvid) + .pipe(untilDestroyed(this)) + .subscribe({ next: datasetFileTree => { this.datasetsOfEnvironment.set(did, [entry, datasetFileTree]); this.datasetFileTrees.push([did, entry.dataset.name, datasetFileTree]); @@ -128,7 +134,10 @@ export class EnvironmentComponent implements OnInit { // related control for dataset link modal onClickOpenDatasetAddModal() { // initialize the datasets info - this.datasetService.retrieveAccessibleDatasets().subscribe({ + this.datasetService + .retrieveAccessibleDatasets() + .pipe(untilDestroyed(this)) + .subscribe({ next: datasets => { this.userAccessibleDatasets = datasets.filter(ds => { const newDid = ds.dataset.did; @@ -164,7 +173,9 @@ export class EnvironmentComponent implements OnInit { onClickAddDataset(dataset: { did: number | undefined; name: string }) { if (this.eid && dataset.did) { - this.environmentService.addDatasetToEnvironment(this.eid, dataset.did).subscribe({ + this.environmentService.addDatasetToEnvironment(this.eid, dataset.did) + .pipe(untilDestroyed(this)) + .subscribe({ next: response => { this.notificationService.success(`Link dataset ${dataset.name} to the environment successfully`); this.showDatasetLinkModal = false; From 6f55c0081f4711824ac1499dc4f2db30784ea775 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Tue, 5 Mar 2024 10:29:22 -0800 Subject: [PATCH 09/10] fmt --- .../component/left-panel/environment/environment.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts index 3d9e33078af..deaff7a1027 100644 --- a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts +++ b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts @@ -1,7 +1,7 @@ import { EnvironmentService } from "../../../../dashboard/user/service/user-environment/environment.service"; import { NotificationService } from "../../../../common/service/notification/notification.service"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; -import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; +import { Component, Input, OnInit } from "@angular/core"; import { WorkflowPersistService } from "../../../../common/service/workflow-persist/workflow-persist.service"; import { WorkflowActionService } from "../../../service/workflow-graph/model/workflow-action.service"; import { @@ -199,7 +199,6 @@ export class EnvironmentComponent implements OnInit { did: dataset.dataset.did, })); } - // console.log(this.filteredLinkingDatasetsName) } // controls of dataset details From d9c4605838a5c4762c3d34f1179339faa29b36c4 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Tue, 5 Mar 2024 15:16:36 -0800 Subject: [PATCH 10/10] fmt frontend --- .../environment/environment.component.ts | 123 +++++++++--------- 1 file changed, 62 insertions(+), 61 deletions(-) diff --git a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts index deaff7a1027..04f17d7eb48 100644 --- a/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts +++ b/core/new-gui/src/app/workspace/component/left-panel/environment/environment.component.ts @@ -96,30 +96,30 @@ export class EnvironmentComponent implements OnInit { if (this.eid) { const eid = this.eid; this.environmentService - .retrieveDatasetsOfEnvironmentDetails(eid) - .pipe(untilDestroyed(this)) - .subscribe({ - next: datasets => { - datasets.forEach(entry => { - const did = entry.dataset.did; - const dvid = entry.version.dvid; - if (did && dvid) { - this.datasetService + .retrieveDatasetsOfEnvironmentDetails(eid) + .pipe(untilDestroyed(this)) + .subscribe({ + next: datasets => { + datasets.forEach(entry => { + const did = entry.dataset.did; + const dvid = entry.version.dvid; + if (did && dvid) { + this.datasetService .retrieveDatasetVersionFileTree(did, dvid) .pipe(untilDestroyed(this)) .subscribe({ - next: datasetFileTree => { - this.datasetsOfEnvironment.set(did, [entry, datasetFileTree]); - this.datasetFileTrees.push([did, entry.dataset.name, datasetFileTree]); - }, - }); - } - }); - }, - error: (err: unknown) => { - this.notificationService.error("Datasets of Environment loading error!"); - }, - }); + next: datasetFileTree => { + this.datasetsOfEnvironment.set(did, [entry, datasetFileTree]); + this.datasetFileTrees.push([did, entry.dataset.name, datasetFileTree]); + }, + }); + } + }); + }, + error: (err: unknown) => { + this.notificationService.error("Datasets of Environment loading error!"); + }, + }); } } @@ -135,36 +135,36 @@ export class EnvironmentComponent implements OnInit { onClickOpenDatasetAddModal() { // initialize the datasets info this.datasetService - .retrieveAccessibleDatasets() - .pipe(untilDestroyed(this)) - .subscribe({ - next: datasets => { - this.userAccessibleDatasets = datasets.filter(ds => { - const newDid = ds.dataset.did; - const newName = ds.dataset.name; - - // Check if the datasetsOfEnvironment does not have the newDid - const didNotExist = newDid && !this.datasetsOfEnvironment.has(newDid); - - // Check if the datasetsOfEnvironment does not have the newName - const nameNotExist = ![...this.datasetsOfEnvironment.values()].some( - ([details, _]) => details.dataset.name === newName - ); - return didNotExist && nameNotExist; - }); + .retrieveAccessibleDatasets() + .pipe(untilDestroyed(this)) + .subscribe({ + next: datasets => { + this.userAccessibleDatasets = datasets.filter(ds => { + const newDid = ds.dataset.did; + const newName = ds.dataset.name; + + // Check if the datasetsOfEnvironment does not have the newDid + const didNotExist = newDid && !this.datasetsOfEnvironment.has(newDid); + + // Check if the datasetsOfEnvironment does not have the newName + const nameNotExist = ![...this.datasetsOfEnvironment.values()].some( + ([details, _]) => details.dataset.name === newName + ); + return didNotExist && nameNotExist; + }); - this.filteredLinkingDatasets = this.userAccessibleDatasets.map(dataset => ({ - name: dataset.dataset.name, - did: dataset.dataset.did, - })); + this.filteredLinkingDatasets = this.userAccessibleDatasets.map(dataset => ({ + name: dataset.dataset.name, + did: dataset.dataset.did, + })); - if (this.userAccessibleDatasets.length == 0) { - this.notificationService.warning("There is no available datasets to be added to the environment."); - } else { - this.showDatasetLinkModal = true; - } - }, - }); + if (this.userAccessibleDatasets.length == 0) { + this.notificationService.warning("There is no available datasets to be added to the environment."); + } else { + this.showDatasetLinkModal = true; + } + }, + }); } handleCancelLinkDataset() { @@ -173,18 +173,19 @@ export class EnvironmentComponent implements OnInit { onClickAddDataset(dataset: { did: number | undefined; name: string }) { if (this.eid && dataset.did) { - this.environmentService.addDatasetToEnvironment(this.eid, dataset.did) - .pipe(untilDestroyed(this)) - .subscribe({ - next: response => { - this.notificationService.success(`Link dataset ${dataset.name} to the environment successfully`); - this.showDatasetLinkModal = false; - this.loadDatasetsOfEnvironment(); - }, - error: (err: unknown) => { - this.notificationService.error(`Linking dataset ${dataset.name} encounters error`); - }, - }); + this.environmentService + .addDatasetToEnvironment(this.eid, dataset.did) + .pipe(untilDestroyed(this)) + .subscribe({ + next: response => { + this.notificationService.success(`Link dataset ${dataset.name} to the environment successfully`); + this.showDatasetLinkModal = false; + this.loadDatasetsOfEnvironment(); + }, + error: (err: unknown) => { + this.notificationService.error(`Linking dataset ${dataset.name} encounters error`); + }, + }); } }