Skip to content

Commit 5972d66

Browse files
committed
feat(metadata): added download metadata logic
1 parent 68e47d2 commit 5972d66

8 files changed

Lines changed: 98 additions & 17 deletions

File tree

src/app/features/files/components/file-metadata/file-metadata.component.spec.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,11 @@ describe('FileMetadataComponent', () => {
133133

134134
it('should open metadata url when file guid exists', () => {
135135
setup({ routeParams: { fileGuid: 'guid-123' } });
136-
const focus = vi.fn();
137-
const openSpy = vi.spyOn(window, 'open').mockReturnValue({ focus } as unknown as Window);
136+
const openSpy = vi.spyOn(window, 'open').mockReturnValue(null);
138137

139138
component.downloadFileMetadata();
140139

141-
expect(openSpy).toHaveBeenCalledWith(expect.stringMatching(/\/metadata\/guid-123$/));
142-
expect(focus).toHaveBeenCalled();
140+
expect(openSpy).toHaveBeenCalledWith(expect.stringMatching(/\/metadata\/guid-123$/), '_blank');
143141
});
144142

145143
it('should not open metadata url when file guid is missing', () => {

src/app/features/files/components/file-metadata/file-metadata.component.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import { ChangeDetectionStrategy, Component, DestroyRef, inject } from '@angular
1111
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
1212
import { ActivatedRoute, Router } from '@angular/router';
1313

14-
import { ENVIRONMENT } from '@core/provider/environment.provider';
1514
import { LanguageLabelPipe } from '@osf/shared/pipes/language-label.pipe';
1615
import { CustomDialogService } from '@osf/shared/services/custom-dialog.service';
16+
import { MetadataRecordsService } from '@osf/shared/services/metadata-records.service';
1717
import { ViewOnlyLinkHelperService } from '@osf/shared/services/view-only-link-helper.service';
1818

1919
import { FileMetadataFields } from '../../constants';
@@ -33,7 +33,7 @@ export class FileMetadataComponent {
3333
private readonly router = inject(Router);
3434
private readonly destroyRef = inject(DestroyRef);
3535
private readonly customDialogService = inject(CustomDialogService);
36-
private readonly environment = inject(ENVIRONMENT);
36+
private readonly metadataRecordsService = inject(MetadataRecordsService);
3737
private readonly viewOnlyService = inject(ViewOnlyLinkHelperService);
3838

3939
private readonly actions = createDispatchMap({ setFileMetadata: SetFileMetadata });
@@ -49,8 +49,10 @@ export class FileMetadataComponent {
4949
readonly metadataFields = FileMetadataFields;
5050

5151
downloadFileMetadata(): void {
52-
if (this.fileGuid()) {
53-
window.open(`${this.environment.webUrl}/metadata/${this.fileGuid()}`)?.focus();
52+
const fileGuid = this.fileGuid();
53+
54+
if (fileGuid) {
55+
this.metadataRecordsService.downloadMetadata(fileGuid);
5456
}
5557
}
5658

src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { toSignal } from '@angular/core/rxjs-interop';
2323
import { ActivatedRoute } from '@angular/router';
2424

2525
import { ENVIRONMENT } from '@core/provider/environment.provider';
26+
import { MetadataRecordsService } from '@osf/shared/services/metadata-records.service';
2627
import { SocialShareService } from '@osf/shared/services/social-share.service';
2728

2829
import { CEDAR_CONFIG, CEDAR_VIEWER_CONFIG } from '../../constants';
@@ -63,6 +64,7 @@ export class CedarTemplateFormComponent {
6364

6465
private route = inject(ActivatedRoute);
6566
readonly environment = inject(ENVIRONMENT);
67+
private readonly metadataRecordsService = inject(MetadataRecordsService);
6668
private readonly socialShareService = inject(SocialShareService);
6769

6870
readonly recordId = signal<string>('');
@@ -136,8 +138,10 @@ export class CedarTemplateFormComponent {
136138
}
137139

138140
downloadMetadadaRecord() {
139-
if (this.fileGuid()) {
140-
window.open(`${this.environment.webUrl}/metadata/${this.fileGuid()}`)?.focus();
141+
const fileGuid = this.fileGuid();
142+
143+
if (fileGuid) {
144+
this.metadataRecordsService.downloadMetadata(fileGuid);
141145
} else {
142146
window.open(this.downloadUrl(), '_blank');
143147
}

src/app/features/project/overview/components/project-overview-metadata/project-overview-metadata.component.html

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,23 @@
77
<div class="metadata-header flex align-items-center justify-content-between">
88
<h2>{{ 'common.labels.metadata' | translate }}</h2>
99

10-
@if (canEdit()) {
10+
<div class="flex gap-2">
1111
<p-button
12-
[routerLink]="'../metadata'"
1312
severity="secondary"
14-
[label]="'common.buttons.edit' | translate"
15-
></p-button>
16-
}
13+
class="btn-icon-only"
14+
icon="fas fa-download"
15+
[attr.aria-label]="'common.buttons.download' | translate"
16+
(onClick)="downloadMetadata()"
17+
/>
18+
19+
@if (canEdit()) {
20+
<p-button
21+
[routerLink]="'../metadata'"
22+
severity="secondary"
23+
[label]="'common.buttons.edit' | translate"
24+
></p-button>
25+
}
26+
</div>
1727
</div>
1828

1929
<div class="flex flex-column gap-2">

src/app/features/project/overview/components/project-overview-metadata/project-overview-metadata.component.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ResourceLicenseComponent } from '@osf/shared/components/resource-licens
1818
import { SubjectsListComponent } from '@osf/shared/components/subjects-list/subjects-list.component';
1919
import { TagsListComponent } from '@osf/shared/components/tags-list/tags-list.component';
2020
import { CurrentResourceType, ResourceType } from '@osf/shared/enums/resource-type.enum';
21+
import { MetadataRecordsService } from '@osf/shared/services/metadata-records.service';
2122
import { CollectionsSelectors, GetProjectSubmissions } from '@osf/shared/stores/collections';
2223
import {
2324
ContributorsSelectors,
@@ -50,6 +51,7 @@ describe('ProjectOverviewMetadataComponent', () => {
5051
let store: Store;
5152
let dispatchMock: Mock;
5253
let mockRouter: RouterMockType;
54+
let metadataRecordsService: { downloadMetadata: Mock };
5355

5456
interface SetupOverrides {
5557
project?: typeof MOCK_PROJECT_OVERVIEW | null;
@@ -58,6 +60,7 @@ describe('ProjectOverviewMetadataComponent', () => {
5860
function setup(overrides: SetupOverrides = {}) {
5961
const project = 'project' in overrides ? overrides.project : MOCK_PROJECT_OVERVIEW;
6062
mockRouter = RouterMockBuilder.create().withUrl('/project/project-1/overview').build();
63+
metadataRecordsService = { downloadMetadata: vi.fn() };
6164

6265
TestBed.configureTestingModule({
6366
imports: [
@@ -77,6 +80,7 @@ describe('ProjectOverviewMetadataComponent', () => {
7780
],
7881
providers: [
7982
provideOSFCore(),
83+
MockProvider(MetadataRecordsService, metadataRecordsService),
8084
MockProvider(Router, mockRouter),
8185
provideMockStore({
8286
signals: [
@@ -166,6 +170,22 @@ describe('ProjectOverviewMetadataComponent', () => {
166170
);
167171
});
168172

173+
it('should download metadata for current project', () => {
174+
setup();
175+
176+
component.downloadMetadata();
177+
178+
expect(metadataRecordsService.downloadMetadata).toHaveBeenCalledWith('project-1');
179+
});
180+
181+
it('should not download metadata when project is missing', () => {
182+
setup({ project: null });
183+
184+
component.downloadMetadata();
185+
186+
expect(metadataRecordsService.downloadMetadata).not.toHaveBeenCalled();
187+
});
188+
169189
it('should navigate to search when clicking a tag', () => {
170190
setup();
171191

src/app/features/project/overview/components/project-overview-metadata/project-overview-metadata.component.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { TruncatedTextComponent } from '@osf/shared/components/truncated-text/tr
2121
import { CurrentResourceType, ResourceType } from '@osf/shared/enums/resource-type.enum';
2222
import { LanguageLabelPipe } from '@osf/shared/pipes/language-label.pipe';
2323
import { ResourceTypeGeneralLabelPipe } from '@osf/shared/pipes/resource-type-general-label.pipe';
24+
import { MetadataRecordsService } from '@osf/shared/services/metadata-records.service';
2425
import { CollectionsSelectors, GetProjectSubmissions } from '@osf/shared/stores/collections';
2526
import {
2627
ContributorsSelectors,
@@ -67,6 +68,7 @@ import { OverviewSupplementsComponent } from '../overview-supplements/overview-s
6768
})
6869
export class ProjectOverviewMetadataComponent {
6970
private readonly router = inject(Router);
71+
private readonly metadataRecordsService = inject(MetadataRecordsService);
7072

7173
readonly currentProject = select(ProjectOverviewSelectors.getProject);
7274
readonly isAnonymous = select(ProjectOverviewSelectors.isProjectAnonymous);
@@ -122,6 +124,14 @@ export class ProjectOverviewMetadataComponent {
122124
});
123125
}
124126

127+
downloadMetadata(): void {
128+
const projectId = this.currentProject()?.id;
129+
130+
if (projectId) {
131+
this.metadataRecordsService.downloadMetadata(projectId);
132+
}
133+
}
134+
125135
onCustomCitationUpdated(citation: string): void {
126136
this.actions.setCustomCitation(citation);
127137
}

src/app/shared/services/metadata-records.service.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,31 @@ describe('MetadataRecordsService', () => {
3131
expect(service.webUrl).toBe(environment.webUrl);
3232
});
3333

34+
it('builds metadata download url', () => {
35+
const osfid = 'ezcuj';
36+
const environment = TestBed.inject(ENVIRONMENT);
37+
38+
expect(service.getMetadataDownloadUrl(osfid)).toBe(`${environment.webUrl}/metadata/${osfid}`);
39+
});
40+
41+
it('opens metadata download url in a new tab', () => {
42+
const osfid = 'ezcuj';
43+
const environment = TestBed.inject(ENVIRONMENT);
44+
const openSpy = vi.spyOn(window, 'open').mockReturnValue(null);
45+
46+
service.downloadMetadata(osfid);
47+
48+
expect(openSpy).toHaveBeenCalledWith(`${environment.webUrl}/metadata/${osfid}`, '_blank');
49+
});
50+
51+
it('does not open metadata download url when osfid is empty', () => {
52+
const openSpy = vi.spyOn(window, 'open').mockReturnValue(null);
53+
54+
service.downloadMetadata('');
55+
56+
expect(openSpy).not.toHaveBeenCalled();
57+
});
58+
3459
it('requests metadata record in text format', () => {
3560
const osfid = 'ezcuj';
3661
let responseBody = '';

src/app/shared/services/metadata-records.service.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,20 @@ export class MetadataRecordsService {
1616
return this.environment.webUrl;
1717
}
1818

19-
getMetadataRecord(osfid: string, format: MetadataRecordFormat) {
20-
const url = `${this.webUrl}/metadata/${osfid}/?format=${format}`;
19+
getMetadataDownloadUrl(id: string): string {
20+
return `${this.webUrl}/metadata/${id}`;
21+
}
22+
23+
downloadMetadata(id: string): void {
24+
if (!id) {
25+
return;
26+
}
27+
28+
window.open(this.getMetadataDownloadUrl(id), '_blank');
29+
}
30+
31+
getMetadataRecord(id: string, format: MetadataRecordFormat) {
32+
const url = `${this.webUrl}/metadata/${id}/?format=${format}`;
2133
return this.http.get(url, {
2234
responseType: 'text',
2335
headers: new HttpHeaders({ 'X-No-Auth-Redirect': 'true' }),

0 commit comments

Comments
 (0)