fixed and tests for equals method

This commit is contained in:
lotte
2019-07-17 16:28:17 +02:00
parent 2bfe6ceebb
commit d16ee88641
23 changed files with 128 additions and 58 deletions

View File

@@ -5,7 +5,7 @@ import { ListableObject } from '../shared/object-collection/shared/listable-obje
/**
* Represents a search result object of a certain (<T>) DSpaceObject
*/
export class MyDSpaceResult<T extends DSpaceObject> implements ListableObject {
export class MyDSpaceResult<T extends DSpaceObject> extends ListableObject {
/**
* The DSpaceObject that was found
*/

View File

@@ -1,7 +1,6 @@
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
import { CacheableObject } from '../../cache/object-cache.reducer';
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
import { EPerson } from './eperson.model';
import { mapsTo, relationship } from '../../cache/builders/build-decorators';
@@ -9,7 +8,7 @@ import { ResourceType } from '../../shared/resource-type';
@mapsTo(EPerson)
@inheritSerialization(NormalizedDSpaceObject)
export class NormalizedEPerson extends NormalizedDSpaceObject<EPerson> implements CacheableObject, ListableObject {
export class NormalizedEPerson extends NormalizedDSpaceObject<EPerson> implements CacheableObject {
/**
* A string representing the unique handle of this EPerson

View File

@@ -1,7 +1,6 @@
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
import { CacheableObject } from '../../cache/object-cache.reducer';
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
import { mapsTo, relationship } from '../../cache/builders/build-decorators';
import { Group } from './group.model';
@@ -9,7 +8,7 @@ import { ResourceType } from '../../shared/resource-type';
@mapsTo(Group)
@inheritSerialization(NormalizedDSpaceObject)
export class NormalizedGroup extends NormalizedDSpaceObject<Group> implements CacheableObject, ListableObject {
export class NormalizedGroup extends NormalizedDSpaceObject<Group> implements CacheableObject {
/**
* List of Groups that this Group belong to
@@ -36,3 +35,4 @@ export class NormalizedGroup extends NormalizedDSpaceObject<Group> implements Ca
@autoserialize
public permanent: boolean;
}

View File

@@ -3,7 +3,7 @@ import { autoserialize } from 'cerialize';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { isNotEmpty } from '../../shared/empty.util';
export class MetadataField implements ListableObject {
export class MetadataField extends ListableObject {
@autoserialize
id: number;

View File

@@ -1,16 +1,13 @@
import { autoserialize } from 'cerialize';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { CacheableObject } from '../cache/object-cache.reducer';
export class MetadataSchema implements ListableObject {
@autoserialize
export class MetadataSchema extends ListableObject {
id: number;
@autoserialize
self: string;
@autoserialize
prefix: string;
@autoserialize
namespace: string;
}

View File

@@ -1,14 +1,13 @@
import { autoserialize } from 'cerialize';
import { NormalizedObject } from '../cache/models/normalized-object.model';
import { mapsTo } from '../cache/builders/build-decorators';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { MetadataSchema } from './metadataschema.model';
/**
* Normalized class for a DSpace MetadataSchema
*/
@mapsTo(MetadataSchema)
export class NormalizedMetadataSchema extends NormalizedObject<MetadataSchema> implements ListableObject {
export class NormalizedMetadataSchema extends NormalizedObject<MetadataSchema> {
/**
* The unique identifier for this schema
*/
@@ -32,4 +31,5 @@ export class NormalizedMetadataSchema extends NormalizedObject<MetadataSchema> i
*/
@autoserialize
namespace: string;
}

View File

@@ -400,7 +400,7 @@ export class RegistryService {
distinctUntilChanged()
);
const serializedSchema = new DSpaceRESTv2Serializer(NormalizedObjectFactory.getConstructor(ResourceType.MetadataSchema)).serialize(schema as NormalizedMetadataSchema);
const serializedSchema = new DSpaceRESTv2Serializer(NormalizedObjectFactory.getConstructor(ResourceType.MetadataSchema)).serialize(schema as any as NormalizedMetadataSchema);
const request$ = endpoint$.pipe(
take(1),

View File

@@ -1,8 +1,8 @@
import { autoserialize, autoserializeAs } from 'cerialize';
import { Equatable } from '../utilities/equatable';
import { hasValue } from '../../shared/empty.util';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { excludeFromEquals } from '../utilities/equals.decorators';
export class BrowseEntry implements Equatable<BrowseEntry> {
export class BrowseEntry extends ListableObject {
@autoserialize
type: string;
@@ -19,11 +19,4 @@ export class BrowseEntry implements Equatable<BrowseEntry> {
@autoserialize
count: number;
equals(other: BrowseEntry): boolean {
if (hasValue(other)) {
return false;
}
return false;
}
}

View File

@@ -12,7 +12,7 @@ import { hasNoValue } from '../../shared/empty.util';
/**
* An abstract model class for a DSpaceObject.
*/
export class DSpaceObject implements CacheableObject, ListableObject {
export class DSpaceObject extends ListableObject implements CacheableObject {
private _name: string;

View File

@@ -1,10 +1,10 @@
import { Store } from '@ngrx/store';
import { SearchSidebarService } from './search-sidebar.service';
import { AppState } from '../../app.reducer';
import { async, TestBed } from '@angular/core/testing';
import { of as observableOf } from 'rxjs';
import { SearchSidebarCollapseAction, SearchSidebarExpandAction } from '../../../shared/search/search-sidebar/search-sidebar.actions';
import { HostWindowService } from '../../shared/host-window.service';
import { AppState } from '../../../app.reducer';
import { HostWindowService } from '../../../shared/host-window.service';
describe('SearchSidebarService', () => {
let service: SearchSidebarService;

View File

@@ -1,7 +1,6 @@
import { Observable } from 'rxjs';
import { CacheableObject } from '../../cache/object-cache.reducer';
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
import { DSpaceObject } from '../../shared/dspace-object.model';
import { EPerson } from '../../eperson/models/eperson.model';
import { RemoteData } from '../../data/remote-data';
@@ -18,7 +17,7 @@ export interface SubmissionObjectError {
/**
* An abstract model class for a SubmissionObject.
*/
export abstract class SubmissionObject extends DSpaceObject implements CacheableObject, ListableObject {
export abstract class SubmissionObject extends DSpaceObject implements CacheableObject {
/**
* The workspaceitem/workflowitem identifier

View File

@@ -2,14 +2,13 @@ import { Observable } from 'rxjs';
import { CacheableObject } from '../../cache/object-cache.reducer';
import { DSpaceObject } from '../../shared/dspace-object.model';
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
import { RemoteData } from '../../data/remote-data';
import { Workflowitem } from '../../submission/models/workflowitem.model';
/**
* An abstract model class for a TaskObject.
*/
export class TaskObject extends DSpaceObject implements CacheableObject, ListableObject {
export class TaskObject extends DSpaceObject implements CacheableObject {
/**
* The task identifier

View File

@@ -34,6 +34,6 @@ export function fieldsForEquals(...fields: string[]): any {
export function getFieldsForEquals(constructor: Function, field: string) {
const fieldMap = excludedFromEquals.get(constructor) || new Map();
const fieldMap = fieldsForEqualsMap.get(constructor) || new Map();
return fieldMap.get(field);
}

View File

@@ -0,0 +1,70 @@
import { excludeFromEquals, fieldsForEquals } from './equals.decorators';
import { EquatableObject } from './equatable';
import { cloneDeep } from 'lodash';
class Dog extends EquatableObject<Dog> {
public name: string;
@excludeFromEquals
public ballsCaught: number;
@fieldsForEquals('name', 'age')
public owner: {
name: string;
age: number;
favouriteFood: string;
}
}
fdescribe('equatable', () => {
let dogRoger: Dog;
let dogMissy: Dog;
beforeEach(() => {
dogRoger = new Dog();
dogRoger.name = 'Roger';
dogRoger.ballsCaught = 6;
dogRoger.owner = { name: 'Tommy', age: 16, favouriteFood: 'spaghetti' };
dogMissy = new Dog();
dogMissy.name = 'Missy';
dogMissy.ballsCaught = 9;
dogMissy.owner = { name: 'Jenny', age: 29, favouriteFood: 'pizza' };
});
it('should return false when the other object is undefined', () => {
const isEqual = dogRoger.equals(undefined);
expect(isEqual).toBe(false);
});
it('should return true when the other object is the exact same object', () => {
const isEqual = dogRoger.equals(dogRoger);
expect(isEqual).toBe(true);
});
it('should return true when the other object is an exact copy of the first one', () => {
const copyOfDogRoger = cloneDeep(dogRoger);
const isEqual = dogRoger.equals(copyOfDogRoger);
expect(isEqual).toBe(true);
});
it('should return false when the other object differs in all fields', () => {
const isEqual = dogRoger.equals(dogMissy);
expect(isEqual).toBe(false);
});
it('should return true when the other object only differs in fields that are marked as excludeFromEquals', () => {
const copyOfDogRoger = cloneDeep(dogRoger);
copyOfDogRoger.ballsCaught = 4;
const isEqual = dogRoger.equals(copyOfDogRoger);
expect(isEqual).toBe(true);
});
it('should return false when the other object differs in fields that are not marked as excludeFromEquals', () => {
const copyOfDogRoger = cloneDeep(dogRoger);
copyOfDogRoger.name = 'Elliot';
const isEqual = dogRoger.equals(copyOfDogRoger);
expect(isEqual).toBe(false);
});
});

View File

@@ -12,11 +12,11 @@ function equalsByFields(object1, object2, fieldList): boolean {
if (hasNoValue(object1[key]) || hasNoValue(object2[key])) {
return true;
}
const mapping = getFieldsForEquals(this.constructor, key);
const mapping = getFieldsForEquals(object1.constructor, key);
if (hasValue(mapping)) {
return !equalsByFields(object1[key], object2[key], mapping);
}
if (this[key] instanceof EquatableObject) {
if (object1[key] instanceof EquatableObject) {
return !object1[key].equals(object2[key]);
}
return object1[key] !== object2[key];
@@ -26,6 +26,12 @@ function equalsByFields(object1, object2, fieldList): boolean {
export abstract class EquatableObject<T> {
equals(other: T): boolean {
if (hasNoValue(other)) {
return false;
}
if (this as any === other) {
return true;
}
const excludedKeys = getExcludedFromEqualsFor(this.constructor);
const keys = Object.keys(this).filter((key) => excludedKeys.findIndex((excludedKey) => key === excludedKey) < 0);
return equalsByFields(this, other, keys);

View File

@@ -8,15 +8,14 @@ import { PaginatedSearchOptions } from '../../../../../search/paginated-search-o
import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model';
import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { hasNoValue, hasValue, isNotEmpty } from '../../../../../empty.util';
import { getSucceededRemoteData } from '../../../../../../core/shared/operators';
import { concat, map, multicast, take, takeWhile, tap } from 'rxjs/operators';
import { hasValue } from '../../../../../empty.util';
import { concat, map, multicast, take, takeWhile } from 'rxjs/operators';
import { Router } from '@angular/router';
import { SEARCH_CONFIG_SERVICE } from '../../../../../../+my-dspace-page/my-dspace-page.component';
import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service';
import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service';
import { ListableObject } from '../../../../../object-collection/shared/listable-object.model';
import { SelectableListState } from '../../../../../object-list/selectable-list/selectable-list.reducer';
import { ListableObject } from '../../../../../object-collection/shared/listable-object.model';
const RELATION_TYPE_FILTER_PREFIX = 'f.entityType=';

View File

@@ -0,0 +1,3 @@
import { EquatableObject } from '../../../core/utilities/equatable';
export class ListableObject extends EquatableObject<ListableObject>{}

View File

@@ -26,10 +26,12 @@ export class TypedItemSearchResultListElementComponent extends SearchResultListE
this.object = obj as ItemSearchResult;
this.dso = this.object.indexableObject;
} else {
this.object = {
this.object = Object.assign(
new ItemSearchResult(),
{
indexableObject: obj as Item,
hitHighlights: new MetadataMap()
};
});
this.dso = obj as Item;
}
this.item = this.dso;

View File

@@ -80,13 +80,13 @@ function selectSingle(state: SelectableListState, action: SelectableListSelectSi
}
function deselect(state: SelectableListState, action: SelectableListDeselectAction) {
const newSelection = state.selection.filter((selected) => hasNoValue(action.payload.find((object) => object.uuid === selected.uuid)));
const newSelection = state.selection.filter((selected) => hasNoValue(action.payload.find((object) => object.equals(selected))));
return Object.assign({}, state, { selection: newSelection });
}
function deselectSingle(state: SelectableListState, action: SelectableListDeselectSingleAction) {
const newSelection = state.selection.filter((selected) => {
return selected.uuid !== action.payload.uuid
return !selected.equals(action.payload);
});
return Object.assign({}, state, { selection: newSelection });
}
@@ -101,5 +101,5 @@ function clearSelection(id: string) {
function isObjectInSelection(selection: ListableObject[], object: ListableObject) {
return selection.findIndex((selected) => selected.uuid === object.uuid) >= 0
return selection.findIndex((selected) => selected.equals(object)) >= 0
}

View File

@@ -5,7 +5,7 @@ import { ListableObject } from '../object-collection/shared/listable-object.mode
/**
* Represents a normalized version of a search result object of a certain DSpaceObject
*/
export class NormalizedSearchResult implements ListableObject {
export class NormalizedSearchResult extends ListableObject {
/**
* The UUID of the DSpaceObject that was found
*/

View File

@@ -2,15 +2,15 @@ import { SearchLabelsComponent } from './search-labels.component';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core';
import { SearchService } from '../search-service/search.service';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { SearchServiceStub } from '../../shared/testing/search-service-stub';
import { Observable, of as observableOf } from 'rxjs';
import { Params } from '@angular/router';
import { ObjectKeysPipe } from '../../shared/utils/object-keys-pipe';
import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service-stub';
import { ObjectKeysPipe } from '../../utils/object-keys-pipe';
import { SearchServiceStub } from '../../testing/search-service-stub';
import { SEARCH_CONFIG_SERVICE } from '../../../+my-dspace-page/my-dspace-page.component';
import { SearchService } from '../../../core/shared/search/search.service';
import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service-stub';
describe('SearchLabelsComponent', () => {
let comp: SearchLabelsComponent;

View File

@@ -5,7 +5,7 @@ import { ListableObject } from '../object-collection/shared/listable-object.mode
/**
* Represents a search result object of a certain (<T>) DSpaceObject
*/
export class SearchResult<T extends DSpaceObject> implements ListableObject {
export class SearchResult<T extends DSpaceObject> extends ListableObject {
/**
* The DSpaceObject that was found
*/

View File

@@ -6,13 +6,16 @@ import { of as observableOf } from 'rxjs';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { SearchSwitchConfigurationComponent } from './search-switch-configuration.component';
import { MYDSPACE_ROUTE, SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service-stub';
import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader';
import { NavigationExtras, Router } from '@angular/router';
import { RouterStub } from '../../shared/testing/router-stub';
import { MyDSpaceConfigurationValueType } from '../../+my-dspace-page/my-dspace-configuration-value-type';
import { SearchService } from '../search-service/search.service';
import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service-stub';
import { RouterStub } from '../../testing/router-stub';
import { SearchService } from '../../../core/shared/search/search.service';
import {
MYDSPACE_ROUTE,
SEARCH_CONFIG_SERVICE
} from '../../../+my-dspace-page/my-dspace-page.component';
import { MyDSpaceConfigurationValueType } from '../../../+my-dspace-page/my-dspace-configuration-value-type';
import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
describe('SearchSwitchConfigurationComponent', () => {