mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
[ACS-437] Update Download UI to use direct access when enabled (#2321)
* [ACS-437] Update Download UI to use direct access when enabled * Refactor * Add unit tests * Refactor by adding separate service * Moved unit tests to ContentUrlService instead of DownloadEffects * Move import in app.module.ts * Fixed review comments * npm install * Fix lint error
This commit is contained in:
parent
26f1cd84ba
commit
882f9de392
@ -51,7 +51,9 @@ import {
|
|||||||
SitesApi,
|
SitesApi,
|
||||||
SearchApi,
|
SearchApi,
|
||||||
PeopleApi,
|
PeopleApi,
|
||||||
VersionsApi
|
VersionsApi,
|
||||||
|
DirectAccessUrlEntry,
|
||||||
|
VersionPaging
|
||||||
} from '@alfresco/js-api';
|
} from '@alfresco/js-api';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
@ -344,11 +346,19 @@ export class ContentApiService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlockNode(nodeId: string, opts?: any) {
|
unlockNode(nodeId: string, opts?: any): Promise<MinimalNodeEntity> {
|
||||||
return this.nodesApi.unlockNode(nodeId, opts);
|
return this.nodesApi.unlockNode(nodeId, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeVersions(nodeId: string, opts?: any) {
|
getNodeVersions(nodeId: string, opts?: any): Observable<VersionPaging> {
|
||||||
return from(this.versionsApi.listVersionHistory(nodeId, opts));
|
return from(this.versionsApi.listVersionHistory(nodeId, opts));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestNodeDirectAccessUrl(nodeId: string): Observable<DirectAccessUrlEntry> {
|
||||||
|
return from(this.nodesApi.requestDirectAccessUrl(nodeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
requestVersionDirectAccessUrl(nodeId: string, versionId: string): Observable<DirectAccessUrlEntry> {
|
||||||
|
return from(this.versionsApi.requestDirectAccessUrl(nodeId, versionId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { TRANSLATION_PROVIDER, CoreModule, AppConfigService, DebugAppConfigService } from '@alfresco/adf-core';
|
import { TRANSLATION_PROVIDER, CoreModule, AppConfigService, DebugAppConfigService } from '@alfresco/adf-core';
|
||||||
import { ContentModule } from '@alfresco/adf-content-services';
|
import { ContentModule, ContentVersionService } from '@alfresco/adf-content-services';
|
||||||
import { SharedModule } from '@alfresco/aca-shared';
|
import { SharedModule } from '@alfresco/aca-shared';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
@ -65,6 +65,7 @@ import { SharedFilesComponent } from './components/shared-files/shared-files.com
|
|||||||
import { CreateFromTemplateDialogComponent } from './dialogs/node-template/create-from-template.dialog';
|
import { CreateFromTemplateDialogComponent } from './dialogs/node-template/create-from-template.dialog';
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { DetailsComponent } from './components/details/details.component';
|
import { DetailsComponent } from './components/details/details.component';
|
||||||
|
import { ContentUrlService } from './services/content-url.service';
|
||||||
|
|
||||||
import { registerLocaleData } from '@angular/common';
|
import { registerLocaleData } from '@angular/common';
|
||||||
import localeFr from '@angular/common/locales/fr';
|
import localeFr from '@angular/common/locales/fr';
|
||||||
@ -149,6 +150,7 @@ registerLocaleData(localeSv);
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: AppConfigService, useClass: DebugAppConfigService },
|
{ provide: AppConfigService, useClass: DebugAppConfigService },
|
||||||
|
{ provide: ContentVersionService, useClass: ContentUrlService },
|
||||||
{
|
{
|
||||||
provide: TRANSLATION_PROVIDER,
|
provide: TRANSLATION_PROVIDER,
|
||||||
multi: true,
|
multi: true,
|
||||||
|
158
src/app/services/content-url.service.spec.ts
Normal file
158
src/app/services/content-url.service.spec.ts
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2020 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { AppTestingModule } from '../testing/app-testing.module';
|
||||||
|
import { MockStore, provideMockStore } from '@ngrx/store/testing';
|
||||||
|
import { ContentUrlService } from './content-url.service';
|
||||||
|
import { getRepositoryStatus } from '@alfresco/aca-shared/store';
|
||||||
|
import { ContentApiService } from '@alfresco/aca-shared';
|
||||||
|
import { of, throwError } from 'rxjs';
|
||||||
|
|
||||||
|
describe('ContentUrlService', () => {
|
||||||
|
let contentUrlService: ContentUrlService;
|
||||||
|
let contentApiService: ContentApiService;
|
||||||
|
let store: MockStore;
|
||||||
|
|
||||||
|
const fakeNodeId = 'fake-node-id';
|
||||||
|
const fakeVersionId = 'fake-version-id';
|
||||||
|
const fakeNodeContentUrl = 'https://api.example.com/fake-node-content-url';
|
||||||
|
const fakeNodeDirectAccessUrl = 'https://remote.example.com/fake-node-content-url';
|
||||||
|
const requestDauMock = {
|
||||||
|
entry: {
|
||||||
|
contentUrl: fakeNodeDirectAccessUrl,
|
||||||
|
attachment: true,
|
||||||
|
expiryTime: new Date()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const requestDauErrorMock = { error: 'fakeError' };
|
||||||
|
|
||||||
|
const overrideSelectorWithEnabledDAU = () => {
|
||||||
|
store.overrideSelector(getRepositoryStatus, {
|
||||||
|
status: {
|
||||||
|
isDirectAccessUrlEnabled: true
|
||||||
|
}
|
||||||
|
} as any);
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [AppTestingModule],
|
||||||
|
providers: [provideMockStore()]
|
||||||
|
});
|
||||||
|
|
||||||
|
contentUrlService = TestBed.inject(ContentUrlService);
|
||||||
|
contentApiService = TestBed.inject(ContentApiService);
|
||||||
|
store = TestBed.inject(MockStore);
|
||||||
|
|
||||||
|
store.overrideSelector(getRepositoryStatus, {
|
||||||
|
status: {
|
||||||
|
isDirectAccessUrlEnabled: false
|
||||||
|
}
|
||||||
|
} as any);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Nodes', () => {
|
||||||
|
let getContentUrlSpy: jasmine.Spy;
|
||||||
|
let requestNodeDirectAccessUrlSpy: jasmine.Spy;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
getContentUrlSpy = spyOn(contentApiService, 'getContentUrl').and.returnValue(fakeNodeContentUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the node URL from the content API if DAU is disabled', async () => {
|
||||||
|
requestNodeDirectAccessUrlSpy = spyOn(contentApiService, 'requestNodeDirectAccessUrl').and.returnValue(of(requestDauMock));
|
||||||
|
|
||||||
|
const url = await contentUrlService.getNodeContentUrl(fakeNodeId).toPromise();
|
||||||
|
|
||||||
|
expect(url).toBe(fakeNodeContentUrl);
|
||||||
|
expect(getContentUrlSpy).toHaveBeenCalledWith(fakeNodeId, true);
|
||||||
|
expect(requestNodeDirectAccessUrlSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the node URL from remote if DAU is enabled', async () => {
|
||||||
|
overrideSelectorWithEnabledDAU();
|
||||||
|
requestNodeDirectAccessUrlSpy = spyOn(contentApiService, 'requestNodeDirectAccessUrl').and.returnValue(of(requestDauMock));
|
||||||
|
|
||||||
|
const url = await contentUrlService.getNodeContentUrl(fakeNodeId).toPromise();
|
||||||
|
|
||||||
|
expect(url).toBe(fakeNodeDirectAccessUrl);
|
||||||
|
expect(getContentUrlSpy).not.toHaveBeenCalled();
|
||||||
|
expect(requestNodeDirectAccessUrlSpy).toHaveBeenCalledWith(fakeNodeId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fallback to getting the node URL from the content API if an error occurs requesting a DAU', async () => {
|
||||||
|
overrideSelectorWithEnabledDAU();
|
||||||
|
requestNodeDirectAccessUrlSpy = spyOn(contentApiService, 'requestNodeDirectAccessUrl').and.returnValue(throwError(requestDauErrorMock));
|
||||||
|
|
||||||
|
const url = await contentUrlService.getNodeContentUrl(fakeNodeId).toPromise();
|
||||||
|
|
||||||
|
expect(url).toBe(fakeNodeContentUrl);
|
||||||
|
expect(getContentUrlSpy).toHaveBeenCalledWith(fakeNodeId, true);
|
||||||
|
expect(requestNodeDirectAccessUrlSpy).toHaveBeenCalledWith(fakeNodeId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Versions', () => {
|
||||||
|
let getVersionContentUrlSpy: jasmine.Spy;
|
||||||
|
let requestVersionDirectAccessUrlSpy: jasmine.Spy;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
getVersionContentUrlSpy = spyOn(contentApiService, 'getVersionContentUrl').and.returnValue(fakeNodeContentUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the version URL from the content API if DAU is disabled', async () => {
|
||||||
|
requestVersionDirectAccessUrlSpy = spyOn(contentApiService, 'requestVersionDirectAccessUrl').and.returnValue(of(requestDauMock));
|
||||||
|
|
||||||
|
const url = await contentUrlService.getVersionContentUrl(fakeNodeId, fakeVersionId).toPromise();
|
||||||
|
|
||||||
|
expect(url).toBe(fakeNodeContentUrl);
|
||||||
|
expect(getVersionContentUrlSpy).toHaveBeenCalledWith(fakeNodeId, fakeVersionId, true);
|
||||||
|
expect(requestVersionDirectAccessUrlSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the version URL from remote if DAU is enabled', async () => {
|
||||||
|
overrideSelectorWithEnabledDAU();
|
||||||
|
requestVersionDirectAccessUrlSpy = spyOn(contentApiService, 'requestVersionDirectAccessUrl').and.returnValue(of(requestDauMock));
|
||||||
|
|
||||||
|
const url = await contentUrlService.getVersionContentUrl(fakeNodeId, fakeVersionId).toPromise();
|
||||||
|
|
||||||
|
expect(url).toBe(fakeNodeDirectAccessUrl);
|
||||||
|
expect(getVersionContentUrlSpy).not.toHaveBeenCalled();
|
||||||
|
expect(requestVersionDirectAccessUrlSpy).toHaveBeenCalledWith(fakeNodeId, fakeVersionId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fallback to getting the version URL from the content API if an error occurs requesting a DAU', async () => {
|
||||||
|
overrideSelectorWithEnabledDAU();
|
||||||
|
requestVersionDirectAccessUrlSpy = spyOn(contentApiService, 'requestVersionDirectAccessUrl').and.returnValue(throwError(requestDauErrorMock));
|
||||||
|
|
||||||
|
const url = await contentUrlService.getVersionContentUrl(fakeNodeId, fakeVersionId).toPromise();
|
||||||
|
|
||||||
|
expect(url).toBe(fakeNodeContentUrl);
|
||||||
|
expect(getVersionContentUrlSpy).toHaveBeenCalledWith(fakeNodeId, fakeVersionId, true);
|
||||||
|
expect(requestVersionDirectAccessUrlSpy).toHaveBeenCalledWith(fakeNodeId, fakeVersionId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
78
src/app/services/content-url.service.ts
Normal file
78
src/app/services/content-url.service.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2020 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { AppStore, getRepositoryStatus } from '@alfresco/aca-shared/store';
|
||||||
|
import { take, map, catchError, mergeMap } from 'rxjs/operators';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { ContentApiService } from '@alfresco/aca-shared';
|
||||||
|
import { DirectAccessUrlEntry } from '@alfresco/js-api';
|
||||||
|
import { ContentVersionService } from '@alfresco/adf-content-services';
|
||||||
|
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class ContentUrlService extends ContentVersionService {
|
||||||
|
constructor(private store: Store<AppStore>, private contentApiService: ContentApiService, alfrescoApiService: AlfrescoApiService) {
|
||||||
|
super(alfrescoApiService);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeContentUrl(nodeId: string, attachment = true): Observable<string> {
|
||||||
|
return this.isDirectAccessUrlEnabled().pipe(
|
||||||
|
mergeMap((dauEnabled) => {
|
||||||
|
if (dauEnabled) {
|
||||||
|
return this.contentApiService.requestNodeDirectAccessUrl(nodeId).pipe(
|
||||||
|
map((dauObj: DirectAccessUrlEntry) => dauObj.entry.contentUrl),
|
||||||
|
catchError(() => of(this.contentApiService.getContentUrl(nodeId, true)))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return of(this.contentApiService.getContentUrl(nodeId, attachment));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getVersionContentUrl(nodeId: string, versionId: string, attachment = true): Observable<string> {
|
||||||
|
return this.isDirectAccessUrlEnabled().pipe(
|
||||||
|
mergeMap((dauEnabled) => {
|
||||||
|
if (dauEnabled) {
|
||||||
|
return this.contentApiService.requestVersionDirectAccessUrl(nodeId, versionId).pipe(
|
||||||
|
map((dauObj: DirectAccessUrlEntry) => dauObj.entry.contentUrl),
|
||||||
|
catchError(() => of(this.contentApiService.getVersionContentUrl(nodeId, versionId, true)))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return of(this.contentApiService.getVersionContentUrl(nodeId, versionId, attachment));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private isDirectAccessUrlEnabled(): Observable<boolean> {
|
||||||
|
return this.store.select(getRepositoryStatus).pipe(
|
||||||
|
take(1),
|
||||||
|
map((repository) => !!repository?.status?.isDirectAccessUrlEnabled)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -32,10 +32,17 @@ import { Actions, Effect, ofType } from '@ngrx/effects';
|
|||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { map, take } from 'rxjs/operators';
|
import { map, take } from 'rxjs/operators';
|
||||||
import { ContentApiService } from '@alfresco/aca-shared';
|
import { ContentApiService } from '@alfresco/aca-shared';
|
||||||
|
import { ContentUrlService } from '../../services/content-url.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DownloadEffects {
|
export class DownloadEffects {
|
||||||
constructor(private store: Store<AppStore>, private actions$: Actions, private contentApi: ContentApiService, private dialog: MatDialog) {}
|
constructor(
|
||||||
|
private store: Store<AppStore>,
|
||||||
|
private actions$: Actions,
|
||||||
|
private contentApi: ContentApiService,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private contentUrlService: ContentUrlService
|
||||||
|
) {}
|
||||||
|
|
||||||
@Effect({ dispatch: false })
|
@Effect({ dispatch: false })
|
||||||
downloadNode$ = this.actions$.pipe(
|
downloadNode$ = this.actions$.pipe(
|
||||||
@ -100,7 +107,9 @@ export class DownloadEffects {
|
|||||||
|
|
||||||
private downloadFile(node: NodeInfo) {
|
private downloadFile(node: NodeInfo) {
|
||||||
if (node && !this.isSharedLinkPreview) {
|
if (node && !this.isSharedLinkPreview) {
|
||||||
this.download(this.contentApi.getContentUrl(node.id, true), node.name);
|
this.contentUrlService.getNodeContentUrl(node.id, true).subscribe((contentUrl) => {
|
||||||
|
this.download(contentUrl, node.name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node && this.isSharedLinkPreview) {
|
if (node && this.isSharedLinkPreview) {
|
||||||
@ -110,7 +119,9 @@ export class DownloadEffects {
|
|||||||
|
|
||||||
private downloadFileVersion(node: NodeInfo, version: Version) {
|
private downloadFileVersion(node: NodeInfo, version: Version) {
|
||||||
if (node && version) {
|
if (node && version) {
|
||||||
this.download(this.contentApi.getVersionContentUrl(node.id, version.id, true), version.name);
|
this.contentUrlService.getVersionContentUrl(node.id, version.id, true).subscribe((contentUrl) => {
|
||||||
|
this.download(contentUrl, node.name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user