Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions frontend/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { AdminGmailComponent } from "./dashboard/component/admin/gmail/admin-gma
import { DatasetDetailComponent } from "./dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component";
import { UserDatasetComponent } from "./dashboard/component/user/user-dataset/user-dataset.component";
import { UserModelComponent } from "./dashboard/component/user/user-model/user-model.component";
import { ModelDetailComponent } from "./dashboard/component/user/user-model/user-model-explorer/model-detail.component";
import { HubWorkflowDetailComponent } from "./hub/component/workflow/detail/hub-workflow-detail.component";
import { LandingPageComponent } from "./hub/component/landing-page/landing-page.component";
import { USER_WORKFLOW } from "./app-routing.constant";
Expand Down Expand Up @@ -140,6 +141,10 @@ routes.push({
path: "model",
component: UserModelComponent,
},
{
path: "model/:mid",
component: ModelDetailComponent,
},
{
path: "compute",
component: UserComputingUnitComponent,
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/app/common/type/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* under the License.
*/

import { DatasetFileNode } from "./datasetVersionFileTree";

export interface Model {
mid: number | undefined;
ownerUid: number | undefined;
Expand All @@ -30,3 +32,13 @@ export interface Model {
framework: string | undefined;
format: string | undefined;
}

export interface ModelVersion {
mvid: number | undefined;
mid: number;
creatorUid: number;
name: string;
versionHash: string | undefined;
creationTime: number | undefined;
fileNodes: DatasetFileNode[] | undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@
nzType="text"
class="action-btn"
title="Download"
*ngIf="entry.type === 'workflow' || entry.type === 'dataset'"
*ngIf="canDownload"
(click)="onClickDownload(); $event.stopPropagation()">
<i
nz-icon
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ describe("CardItemComponent", () => {
it("onClickDownload downloads a workflow via the download service", () => {
const downloadService = TestBed.inject(DownloadService);
const downloadWorkflowSpy = vi.spyOn(downloadService, "downloadWorkflow").mockReturnValue(of({} as any));
component.entry = makeWorkflowEntry({ id: 7, workflow: { isOwner: true, workflow: { name: "myflow" } } } as any);
component.entry = makeWorkflowEntry({ id: 7, name: "myflow" });

component.onClickDownload();

Expand Down Expand Up @@ -909,6 +909,7 @@ describe("CardItemComponent", () => {
component.entry = makeWorkflowEntry();
component.isPrivateSearch = true;
component.currentUid = 1;
component.initializeEntry(); // the Download button reads a per-kind capability off the entry
fixture.detectChanges();

const de = fixture.debugElement;
Expand All @@ -927,6 +928,7 @@ describe("CardItemComponent", () => {
component.entry = makeWorkflowEntry();
component.isPrivateSearch = true;
component.currentUid = 1;
component.initializeEntry();
fixture.detectChanges();

const detailSpy = vi.spyOn(component, "openDetailModal").mockImplementation(() => {});
Expand Down Expand Up @@ -1029,6 +1031,7 @@ describe("CardItemComponent", () => {
it("shows Download but hides Detail/Copy/checkbox for a dataset in private mode", () => {
component.entry = makeDatasetEntry();
component.isPrivateSearch = true;
component.initializeEntry();
fixture.detectChanges();

const de = fixture.debugElement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import { WorkflowPersistService } from "src/app/common/service/workflow-persist/
import { firstValueFrom } from "rxjs";
import { HubWorkflowDetailComponent } from "../../../../../hub/component/workflow/detail/hub-workflow-detail.component";
import { ActionType, HubService } from "../../../../../hub/service/hub.service";
import { DownloadService } from "src/app/dashboard/service/user/download/download.service";
import { formatSize } from "src/app/common/util/size-formatter.util";
import { formatRelativeTime, formatCount } from "src/app/common/util/format.util";
import { DatasetService } from "../../../../service/user/dataset/dataset.service";
Expand Down Expand Up @@ -78,6 +77,7 @@ export class CardItemComponent implements OnChanges {
public originalName: string = "";
public originalDescription: string | undefined = undefined;
public disableDelete: boolean = false;
public canDownload: boolean = false;
@Input() currentUid: number | undefined;
@ViewChild("nameInput") nameInput!: ElementRef;
@ViewChild("descriptionInput") descriptionInput!: ElementRef;
Expand Down Expand Up @@ -125,7 +125,6 @@ export class CardItemComponent implements OnChanges {
private datasetService: DatasetService,
private modal: NzModalService,
private hubService: HubService,
private downloadService: DownloadService,
private cdr: ChangeDetectorRef,
private notificationService: NotificationService,
private workflowCoverService: WorkflowCoverService,
Expand Down Expand Up @@ -191,6 +190,7 @@ export class CardItemComponent implements OnChanges {
const descriptor = this.resourceRegistry.get(this.entry.type);
this.iconType = descriptor.iconType;
this.disableDelete = !descriptor.isOwner(this.entry);
this.canDownload = descriptor.download !== undefined;
this.entryLink = this.resourceRegistry.entryLink(this.entry, this.currentUid);
if (descriptor.hasSize && typeof this.entry.id === "number") {
this.size = this.entry.size;
Expand Down Expand Up @@ -285,16 +285,9 @@ export class CardItemComponent implements OnChanges {
}

public onClickDownload = (): void => {
if (!this.entry.id) return;

if (this.entry.type === "workflow") {
this.downloadService
.downloadWorkflow(this.entry.id, this.entry.workflow.workflow.name)
.pipe(untilDestroyed(this))
.subscribe();
} else if (this.entry.type === "dataset") {
this.downloadService.downloadDataset(this.entry.id, this.entry.name).pipe(untilDestroyed(this)).subscribe();
}
const download = this.resourceRegistry.get(this.entry.type).download;
if (!this.entry.id || !download) return;
download(this.entry.id, this.entry.name).pipe(untilDestroyed(this)).subscribe();
};

onEditName(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
<button
nz-button
nzType="text"
*ngIf="entry.type === 'workflow' || entry.type === 'dataset'"
*ngIf="canDownload"
title="Download"
(click)="onClickDownload()">
<i
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

import { ComponentFixture, TestBed } from "@angular/core/testing";
import { DownloadService } from "src/app/dashboard/service/user/download/download.service";
import { By } from "@angular/platform-browser";
import { ListItemComponent } from "./list-item.component";
import {
Expand Down Expand Up @@ -566,27 +567,40 @@ describe("ListItemComponent", () => {

describe("download", () => {
it("downloads a workflow by id and name", () => {
const download = vi
.spyOn((component as any).downloadService, "downloadWorkflow")
.mockReturnValue(of(undefined));
feed(entryOf({ type: "workflow", workflow: { isOwner: true, workflow: { name: "flow" } } }));
const download = vi.spyOn(TestBed.inject(DownloadService), "downloadWorkflow").mockReturnValue(of({} as any));
feed(entryOf({ type: "workflow", name: "flow", workflow: { isOwner: true } }));

component.onClickDownload();

expect(download).toHaveBeenCalledWith(7, "flow");
});

it("downloads a dataset by id and name", () => {
const download = vi.spyOn((component as any).downloadService, "downloadDataset").mockReturnValue(of(undefined));
const download = vi.spyOn(TestBed.inject(DownloadService), "downloadDataset").mockReturnValue(of(new Blob()));
feed(entryOf({ type: "dataset", dataset: { isOwner: true }, name: "set" }));

component.onClickDownload();

expect(download).toHaveBeenCalledWith(7, "set");
});

it("downloads a renamed workflow under its new name", () => {
// The rename writes entry.name and leaves entry.workflow.workflow.name stale, so
// reading the payload here used to name the zip after the pre-rename workflow.
(workflowPersistService as any).updateWorkflowName.mockReturnValue(of({} as Response));
const download = vi.spyOn(TestBed.inject(DownloadService), "downloadWorkflow").mockReturnValue(of({} as any));
feed(
entryOf({ type: "workflow", name: "old-name", workflow: { isOwner: true, workflow: { name: "old-name" } } })
);

component.confirmUpdateCustomName("new-name");
component.onClickDownload();

expect(download).toHaveBeenCalledWith(7, "new-name");
});

it("downloads nothing for an entry that was never persisted", () => {
const workflow = vi.spyOn((component as any).downloadService, "downloadWorkflow");
const workflow = vi.spyOn(TestBed.inject(DownloadService), "downloadWorkflow");
feed(entryOf({ type: "file", id: 0 }));

component.onClickDownload();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import { WorkflowPersistService } from "src/app/common/service/workflow-persist/
import { firstValueFrom } from "rxjs";
import { HubWorkflowDetailComponent } from "../../../../hub/component/workflow/detail/hub-workflow-detail.component";
import { ActionType, HubService } from "../../../../hub/service/hub.service";
import { DownloadService } from "src/app/dashboard/service/user/download/download.service";
import { formatSize } from "src/app/common/util/size-formatter.util";
import { formatCount, formatRelativeTime } from "src/app/common/util/format.util";
import { DatasetService } from "../../../service/user/dataset/dataset.service";
Expand Down Expand Up @@ -84,6 +83,7 @@ export class ListItemComponent implements OnChanges {
public originalName: string = "";
public originalDescription: string | undefined = undefined;
public disableDelete: boolean = false;
public canDownload: boolean = false;
@Input() currentUid: number | undefined;
@ViewChild("nameInput") nameInput!: ElementRef;
@ViewChild("descriptionInput") descriptionInput!: ElementRef;
Expand Down Expand Up @@ -125,7 +125,6 @@ export class ListItemComponent implements OnChanges {
private datasetService: DatasetService,
private modal: NzModalService,
private hubService: HubService,
private downloadService: DownloadService,
private cdr: ChangeDetectorRef,
private notificationService: NotificationService,
private resourceRegistry: ResourceRegistryService
Expand All @@ -135,6 +134,7 @@ export class ListItemComponent implements OnChanges {
const descriptor = this.resourceRegistry.get(this.entry.type);
this.iconType = descriptor.iconType;
this.disableDelete = !descriptor.isOwner(this.entry);
this.canDownload = descriptor.download !== undefined;
this.entryLink = this.resourceRegistry.entryLink(this.entry, this.currentUid);
if (descriptor.hasSize && typeof this.entry.id === "number") {
this.size = this.entry.size;
Expand Down Expand Up @@ -211,16 +211,9 @@ export class ListItemComponent implements OnChanges {
}

public onClickDownload = (): void => {
if (!this.entry.id) return;

if (this.entry.type === "workflow") {
this.downloadService
.downloadWorkflow(this.entry.id, this.entry.workflow.workflow.name)
.pipe(untilDestroyed(this))
.subscribe();
} else if (this.entry.type === "dataset") {
this.downloadService.downloadDataset(this.entry.id, this.entry.name).pipe(untilDestroyed(this)).subscribe();
}
const download = this.resourceRegistry.get(this.entry.type).download;
if (!this.entry.id || !download) return;
download(this.entry.id, this.entry.name).pipe(untilDestroyed(this)).subscribe();
};

onEditName(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,8 @@ <h3 class="file-title">
<texera-user-dataset-file-renderer
*ngIf="selectedVersion"
[isMaximized]="isMaximized"
[did]="did"
[dvid]="selectedVersion.dvid"
[resourceId]="did"
[versionId]="selectedVersion.dvid"
[filePath]="currentDisplayedFileName"
[fileSize]="currentFileSize"
[isLogin]="isLogin"
Expand Down
Loading
Loading