mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 10:34:15 +00:00
Merge branch 'w2p-120109_fix-findByHref-and-findListByHref-skipping-their-response_contribute-7.6' into dspace-7_x
# Conflicts: # src/app/core/data/base/base-data.service.spec.ts # src/app/core/data/base/base-data.service.ts # src/app/item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts
This commit is contained in:
@@ -55,7 +55,6 @@ import { CommunityBreadcrumbResolver } from '../core/breadcrumbs/community-bread
|
|||||||
resolve: {
|
resolve: {
|
||||||
dso: CollectionPageResolver,
|
dso: CollectionPageResolver,
|
||||||
breadcrumb: CollectionBreadcrumbResolver,
|
breadcrumb: CollectionBreadcrumbResolver,
|
||||||
menu: DSOEditMenuResolver
|
|
||||||
},
|
},
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
children: [
|
children: [
|
||||||
@@ -85,6 +84,9 @@ import { CommunityBreadcrumbResolver } from '../core/breadcrumbs/community-bread
|
|||||||
path: '',
|
path: '',
|
||||||
component: ThemedCollectionPageComponent,
|
component: ThemedCollectionPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
|
resolve: {
|
||||||
|
menu: DSOEditMenuResolver,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data: {
|
data: {
|
||||||
|
@@ -48,7 +48,6 @@ import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso
|
|||||||
resolve: {
|
resolve: {
|
||||||
dso: CommunityPageResolver,
|
dso: CommunityPageResolver,
|
||||||
breadcrumb: CommunityBreadcrumbResolver,
|
breadcrumb: CommunityBreadcrumbResolver,
|
||||||
menu: DSOEditMenuResolver
|
|
||||||
},
|
},
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
children: [
|
children: [
|
||||||
@@ -68,6 +67,9 @@ import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso
|
|||||||
path: '',
|
path: '',
|
||||||
component: ThemedCommunityPageComponent,
|
component: ThemedCommunityPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
|
resolve: {
|
||||||
|
menu: DSOEditMenuResolver,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data: {
|
data: {
|
||||||
|
@@ -50,6 +50,7 @@ describe('BaseDataService', () => {
|
|||||||
let selfLink;
|
let selfLink;
|
||||||
let linksToFollow;
|
let linksToFollow;
|
||||||
let testScheduler;
|
let testScheduler;
|
||||||
|
let remoteDataTimestamp: number;
|
||||||
let remoteDataMocks;
|
let remoteDataMocks;
|
||||||
|
|
||||||
function initTestService(): TestService {
|
function initTestService(): TestService {
|
||||||
@@ -86,20 +87,22 @@ describe('BaseDataService', () => {
|
|||||||
expect(actual).toEqual(expected);
|
expect(actual).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
const timeStamp = new Date().getTime();
|
// The response's lastUpdated equals the time of 60 seconds after the test started, ensuring they are not perceived
|
||||||
|
// as cached values.
|
||||||
|
remoteDataTimestamp = new Date().getTime() + 60 * 1000;
|
||||||
const msToLive = 15 * 60 * 1000;
|
const msToLive = 15 * 60 * 1000;
|
||||||
const payload = { foo: 'bar' };
|
const payload = { foo: 'bar' };
|
||||||
const statusCodeSuccess = 200;
|
const statusCodeSuccess = 200;
|
||||||
const statusCodeError = 404;
|
const statusCodeError = 404;
|
||||||
const errorMessage = 'not found';
|
const errorMessage = 'not found';
|
||||||
remoteDataMocks = {
|
remoteDataMocks = {
|
||||||
RequestPending: new RemoteData(undefined, msToLive, timeStamp, RequestEntryState.RequestPending, undefined, undefined, undefined),
|
RequestPending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.RequestPending, undefined, undefined, undefined),
|
||||||
ResponsePending: new RemoteData(undefined, msToLive, timeStamp, RequestEntryState.ResponsePending, undefined, undefined, undefined),
|
ResponsePending: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.ResponsePending, undefined, undefined, undefined),
|
||||||
ResponsePendingStale: new RemoteData(undefined, msToLive, timeStamp, RequestEntryState.ResponsePendingStale, undefined, undefined, undefined),
|
ResponsePendingStale: new RemoteData(undefined, msToLive, remoteDataTimestamp, RequestEntryState.ResponsePendingStale, undefined, undefined, undefined),
|
||||||
Success: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.Success, undefined, payload, statusCodeSuccess),
|
Success: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.Success, undefined, payload, statusCodeSuccess),
|
||||||
SuccessStale: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.SuccessStale, undefined, payload, statusCodeSuccess),
|
SuccessStale: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.SuccessStale, undefined, payload, statusCodeSuccess),
|
||||||
Error: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.Error, errorMessage, undefined, statusCodeError),
|
Error: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.Error, errorMessage, undefined, statusCodeError),
|
||||||
ErrorStale: new RemoteData(timeStamp, msToLive, timeStamp, RequestEntryState.ErrorStale, errorMessage, undefined, statusCodeError),
|
ErrorStale: new RemoteData(remoteDataTimestamp, msToLive, remoteDataTimestamp, RequestEntryState.ErrorStale, errorMessage, undefined, statusCodeError),
|
||||||
};
|
};
|
||||||
|
|
||||||
return new TestService(
|
return new TestService(
|
||||||
@@ -333,11 +336,15 @@ describe('BaseDataService', () => {
|
|||||||
spyOn(service as any, 'reRequestStaleRemoteData').and.callFake(() => (source) => source);
|
spyOn(service as any, 'reRequestStaleRemoteData').and.callFake(() => (source) => source);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not emit a cached completed RemoteData', () => {
|
||||||
it(`should not emit a cached completed RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
|
// Old cached value from 1 minute before the test started
|
||||||
|
const oldCachedSucceededData: RemoteData<any> = Object.assign({}, remoteDataMocks.Success, {
|
||||||
|
timeCompleted: remoteDataTimestamp - 2 * 60 * 1000,
|
||||||
|
lastUpdated: remoteDataTimestamp - 2 * 60 * 1000,
|
||||||
|
} as RemoteData<any>);
|
||||||
testScheduler.run(({ cold, expectObservable }) => {
|
testScheduler.run(({ cold, expectObservable }) => {
|
||||||
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b-c-d-e', {
|
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b-c-d-e', {
|
||||||
a: remoteDataMocks.Success,
|
a: oldCachedSucceededData,
|
||||||
b: remoteDataMocks.RequestPending,
|
b: remoteDataMocks.RequestPending,
|
||||||
c: remoteDataMocks.ResponsePending,
|
c: remoteDataMocks.ResponsePending,
|
||||||
d: remoteDataMocks.Success,
|
d: remoteDataMocks.Success,
|
||||||
@@ -355,6 +362,22 @@ describe('BaseDataService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should emit the first completed RemoteData since the request was made', () => {
|
||||||
|
testScheduler.run(({ cold, expectObservable }) => {
|
||||||
|
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b', {
|
||||||
|
a: remoteDataMocks.Success,
|
||||||
|
b: remoteDataMocks.SuccessStale,
|
||||||
|
}));
|
||||||
|
const expected = 'a-b';
|
||||||
|
const values = {
|
||||||
|
a: remoteDataMocks.Success,
|
||||||
|
b: remoteDataMocks.SuccessStale,
|
||||||
|
};
|
||||||
|
|
||||||
|
expectObservable(service.findByHref(selfLink, false, true, ...linksToFollow)).toBe(expected, values);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it(`should not emit a cached stale RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
|
it(`should not emit a cached stale RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
|
||||||
testScheduler.run(({ cold, expectObservable }) => {
|
testScheduler.run(({ cold, expectObservable }) => {
|
||||||
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b-c-d-e-f-g', {
|
spyOn(rdbService, 'buildSingle').and.returnValue(cold('a-b-c-d-e-f-g', {
|
||||||
@@ -521,11 +544,15 @@ describe('BaseDataService', () => {
|
|||||||
spyOn(service as any, 'reRequestStaleRemoteData').and.callFake(() => (source) => source);
|
spyOn(service as any, 'reRequestStaleRemoteData').and.callFake(() => (source) => source);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not emit a cached completed RemoteData', () => {
|
||||||
it(`should not emit a cached completed RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
|
|
||||||
testScheduler.run(({ cold, expectObservable }) => {
|
testScheduler.run(({ cold, expectObservable }) => {
|
||||||
|
// Old cached value from 1 minute before the test started
|
||||||
|
const oldCachedSucceededData: RemoteData<any> = Object.assign({}, remoteDataMocks.Success, {
|
||||||
|
timeCompleted: remoteDataTimestamp - 2 * 60 * 1000,
|
||||||
|
lastUpdated: remoteDataTimestamp - 2 * 60 * 1000,
|
||||||
|
} as RemoteData<any>);
|
||||||
spyOn(rdbService, 'buildList').and.returnValue(cold('a-b-c-d-e', {
|
spyOn(rdbService, 'buildList').and.returnValue(cold('a-b-c-d-e', {
|
||||||
a: remoteDataMocks.Success,
|
a: oldCachedSucceededData,
|
||||||
b: remoteDataMocks.RequestPending,
|
b: remoteDataMocks.RequestPending,
|
||||||
c: remoteDataMocks.ResponsePending,
|
c: remoteDataMocks.ResponsePending,
|
||||||
d: remoteDataMocks.Success,
|
d: remoteDataMocks.Success,
|
||||||
@@ -543,6 +570,22 @@ describe('BaseDataService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should emit the first completed RemoteData since the request was made', () => {
|
||||||
|
testScheduler.run(({ cold, expectObservable }) => {
|
||||||
|
spyOn(rdbService, 'buildList').and.returnValue(cold('a-b', {
|
||||||
|
a: remoteDataMocks.Success,
|
||||||
|
b: remoteDataMocks.SuccessStale,
|
||||||
|
}));
|
||||||
|
const expected = 'a-b';
|
||||||
|
const values = {
|
||||||
|
a: remoteDataMocks.Success,
|
||||||
|
b: remoteDataMocks.SuccessStale,
|
||||||
|
};
|
||||||
|
|
||||||
|
expectObservable(service.findListByHref(selfLink, findListOptions, false, true, ...linksToFollow)).toBe(expected, values);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it(`should not emit a cached stale RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
|
it(`should not emit a cached stale RemoteData, but only start emitting after the state first changes to RequestPending`, () => {
|
||||||
testScheduler.run(({ cold, expectObservable }) => {
|
testScheduler.run(({ cold, expectObservable }) => {
|
||||||
spyOn(rdbService, 'buildList').and.returnValue(cold('a-b-c-d-e-f-g', {
|
spyOn(rdbService, 'buildList').and.returnValue(cold('a-b-c-d-e-f-g', {
|
||||||
|
@@ -266,6 +266,7 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
|
|||||||
map((href: string) => this.buildHrefFromFindOptions(href, {}, [], ...linksToFollow)),
|
map((href: string) => this.buildHrefFromFindOptions(href, {}, [], ...linksToFollow)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const startTime: number = new Date().getTime();
|
||||||
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
|
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
|
||||||
|
|
||||||
return this.rdbService.buildSingle<T>(requestHref$, ...linksToFollow).pipe(
|
return this.rdbService.buildSingle<T>(requestHref$, ...linksToFollow).pipe(
|
||||||
@@ -273,7 +274,7 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
|
|||||||
// call it isn't immediately returned, but we wait until the remote data for the new request
|
// call it isn't immediately returned, but we wait until the remote data for the new request
|
||||||
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
|
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
|
||||||
// cached completed object
|
// cached completed object
|
||||||
skipWhile((rd: RemoteData<T>) => rd.isStale || (!useCachedVersionIfAvailable && rd.hasCompleted)),
|
skipWhile((rd: RemoteData<T>) => rd.isStale || (!useCachedVersionIfAvailable && rd.lastUpdated < startTime)),
|
||||||
this.reRequestStaleRemoteData(reRequestOnStale, () =>
|
this.reRequestStaleRemoteData(reRequestOnStale, () =>
|
||||||
this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
|
this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
|
||||||
);
|
);
|
||||||
@@ -300,6 +301,7 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
|
|||||||
map((href: string) => this.buildHrefFromFindOptions(href, options, [], ...linksToFollow)),
|
map((href: string) => this.buildHrefFromFindOptions(href, options, [], ...linksToFollow)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const startTime: number = new Date().getTime();
|
||||||
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
|
this.createAndSendGetRequest(requestHref$, useCachedVersionIfAvailable);
|
||||||
|
|
||||||
return this.rdbService.buildList<T>(requestHref$, ...linksToFollow).pipe(
|
return this.rdbService.buildList<T>(requestHref$, ...linksToFollow).pipe(
|
||||||
@@ -307,7 +309,7 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
|
|||||||
// call it isn't immediately returned, but we wait until the remote data for the new request
|
// call it isn't immediately returned, but we wait until the remote data for the new request
|
||||||
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
|
// is created. If useCachedVersionIfAvailable is false it also ensures you don't get a
|
||||||
// cached completed object
|
// cached completed object
|
||||||
skipWhile((rd: RemoteData<PaginatedList<T>>) => rd.isStale || (!useCachedVersionIfAvailable && rd.hasCompleted)),
|
skipWhile((rd: RemoteData<PaginatedList<T>>) => rd.isStale || (!useCachedVersionIfAvailable && rd.lastUpdated < startTime)),
|
||||||
this.reRequestStaleRemoteData(reRequestOnStale, () =>
|
this.reRequestStaleRemoteData(reRequestOnStale, () =>
|
||||||
this.findListByHref(href$, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
|
this.findListByHref(href$, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
|
||||||
);
|
);
|
||||||
|
@@ -6,7 +6,7 @@ import { ObjectUpdatesService } from '../../../core/data/object-updates/object-u
|
|||||||
import { ActivatedRoute, Router, Data } from '@angular/router';
|
import { ActivatedRoute, Router, Data } from '@angular/router';
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { first, map, switchMap, tap } from 'rxjs/operators';
|
import { take, map, switchMap, tap } from 'rxjs/operators';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { AbstractTrackableComponent } from '../../../shared/trackable/abstract-trackable.component';
|
import { AbstractTrackableComponent } from '../../../shared/trackable/abstract-trackable.component';
|
||||||
import { environment } from '../../../../environments/environment';
|
import { environment } from '../../../../environments/environment';
|
||||||
@@ -82,7 +82,7 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
|
|||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
|
|
||||||
this.discardTimeOut = environment.item.edit.undoTimeout;
|
this.discardTimeOut = environment.item.edit.undoTimeout;
|
||||||
this.hasChanges().pipe(first()).subscribe((hasChanges) => {
|
this.hasChanges().pipe(take(1)).subscribe((hasChanges) => {
|
||||||
if (!hasChanges) {
|
if (!hasChanges) {
|
||||||
this.initializeOriginalFields();
|
this.initializeOriginalFields();
|
||||||
} else {
|
} else {
|
||||||
@@ -167,7 +167,7 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
|
|||||||
*/
|
*/
|
||||||
private checkLastModified() {
|
private checkLastModified() {
|
||||||
const currentVersion = this.item.lastModified;
|
const currentVersion = this.item.lastModified;
|
||||||
this.objectUpdatesService.getLastModified(this.url).pipe(first()).subscribe(
|
this.objectUpdatesService.getLastModified(this.url).pipe(take(1)).subscribe(
|
||||||
(updateVersion: Date) => {
|
(updateVersion: Date) => {
|
||||||
if (updateVersion.getDate() !== currentVersion.getDate()) {
|
if (updateVersion.getDate() !== currentVersion.getDate()) {
|
||||||
this.notificationsService.warning(this.getNotificationTitle('outdated'), this.getNotificationContent('outdated'));
|
this.notificationsService.warning(this.getNotificationTitle('outdated'), this.getNotificationContent('outdated'));
|
||||||
|
@@ -28,7 +28,6 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
|||||||
resolve: {
|
resolve: {
|
||||||
dso: ItemPageResolver,
|
dso: ItemPageResolver,
|
||||||
breadcrumb: ItemBreadcrumbResolver,
|
breadcrumb: ItemBreadcrumbResolver,
|
||||||
menu: DSOEditMenuResolver
|
|
||||||
},
|
},
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
children: [
|
children: [
|
||||||
@@ -36,10 +35,16 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
|||||||
path: '',
|
path: '',
|
||||||
component: ThemedItemPageComponent,
|
component: ThemedItemPageComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
|
resolve: {
|
||||||
|
menu: DSOEditMenuResolver,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'full',
|
path: 'full',
|
||||||
component: ThemedFullItemPageComponent,
|
component: ThemedFullItemPageComponent,
|
||||||
|
resolve: {
|
||||||
|
menu: DSOEditMenuResolver,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ITEM_EDIT_PATH,
|
path: ITEM_EDIT_PATH,
|
||||||
|
Reference in New Issue
Block a user