fixed lint errors

This commit is contained in:
William Welling
2017-07-13 11:19:02 -05:00
parent 75cb60e70f
commit 066bba28af
123 changed files with 1295 additions and 1407 deletions

View File

@@ -9,21 +9,21 @@ import {
import {
CUSTOM_ELEMENTS_SCHEMA,
DebugElement
} from "@angular/core";
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { By } from '@angular/platform-browser';
import { TranslateModule, TranslateLoader } from "@ngx-translate/core";
import { Store, StoreModule } from "@ngrx/store";
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { Store, StoreModule } from '@ngrx/store';
// Load the implementations that should be tested
import { AppComponent } from './app.component';
import { HostWindowState } from "./shared/host-window.reducer";
import { HostWindowResizeAction } from "./shared/host-window.actions";
import { MockTranslateLoader } from "./shared/testing/mock-translate-loader";
import { HostWindowState } from './shared/host-window.reducer';
import { HostWindowResizeAction } from './shared/host-window.actions';
import { MockTranslateLoader } from './shared/testing/mock-translate-loader';
import { BrowserCookiesModule } from '../modules/cookies/browser-cookies.module';
import { BrowserDataLoaderModule } from '../modules/data-loader/browser-data-loader.module';
@@ -31,7 +31,7 @@ import { BrowserTransferStateModule } from '../modules/transfer-state/browser-tr
import { BrowserTransferStoreModule } from '../modules/transfer-store/browser-transfer-store.module';
import { GLOBAL_CONFIG, ENV_CONFIG } from '../config';
import { NativeWindowRef, NativeWindowService } from "./shared/window.service";
import { NativeWindowRef, NativeWindowService } from './shared/window.service';
let comp: AppComponent;
let fixture: ComponentFixture<AppComponent>;
@@ -73,7 +73,7 @@ describe('App component', () => {
comp = fixture.componentInstance; // component test instance
// query for the <div class="outer-wrapper"> by CSS element selector
// query for the <div class='outer-wrapper'> by CSS element selector
de = fixture.debugElement.query(By.css('div.outer-wrapper'));
el = de.nativeElement;
});
@@ -83,7 +83,7 @@ describe('App component', () => {
expect(app).toBeTruthy();
}));
describe("when the window is resized", () => {
describe('when the window is resized', () => {
let width: number;
let height: number;
let store: Store<HostWindowState>;
@@ -97,7 +97,7 @@ describe('App component', () => {
height = window.innerHeight;
});
it("should dispatch a HostWindowResizeAction with the width and height of the window as its payload", () => {
it('should dispatch a HostWindowResizeAction with the width and height of the window as its payload', () => {
expect(store.dispatch).toHaveBeenCalledWith(new HostWindowResizeAction(width, height));
});

View File

@@ -5,19 +5,19 @@ import {
ViewEncapsulation,
OnInit,
HostListener
} from "@angular/core";
} from '@angular/core';
import { TranslateService } from "@ngx-translate/core";
import { TranslateService } from '@ngx-translate/core';
import { Store } from "@ngrx/store";
import { Store } from '@ngrx/store';
import { TransferState } from '../modules/transfer-state/transfer-state';
import { HostWindowState } from "./shared/host-window.reducer";
import { HostWindowState } from './shared/host-window.reducer';
import { HostWindowResizeAction } from "./shared/host-window.actions";
import { HostWindowResizeAction } from './shared/host-window.actions';
import { NativeWindowRef, NativeWindowService } from "./shared/window.service";
import { NativeWindowRef, NativeWindowService } from './shared/window.service';
import { GLOBAL_CONFIG, GlobalConfig } from '../config';
@@ -54,8 +54,8 @@ export class AppComponent implements OnInit {
}
ngOnInit() {
const env: string = this.config.production ? "Production" : "Development";
const color: string = this.config.production ? "red" : "green";
const env: string = this.config.production ? 'Production' : 'Development';
const color: string = this.config.production ? 'red' : 'green';
console.info(`Environment: %c${env}`, `color: ${color}; font-weight: bold;`);
}

View File

@@ -1,10 +1,11 @@
import { EffectsModule } from "@ngrx/effects";
import { HeaderEffects } from "./header/header.effects";
import { StoreEffects } from "./store.effects";
import { coreEffects } from "./core/core.effects";
import { EffectsModule } from '@ngrx/effects';
import { HeaderEffects } from './header/header.effects';
import { StoreEffects } from './store.effects';
import { coreEffects } from './core/core.effects';
export const effects = [
...coreEffects, //TODO should probably be imported in coreModule
...coreEffects, // TODO: should probably be imported in coreModule
EffectsModule.run(StoreEffects),
EffectsModule.run(HeaderEffects)
];

View File

@@ -2,9 +2,9 @@ import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';
import { StoreModule, Store } from "@ngrx/store";
import { RouterStoreModule } from "@ngrx/router-store";
import { StoreDevtoolsModule } from "@ngrx/store-devtools";
import { StoreModule, Store } from '@ngrx/store';
import { RouterStoreModule } from '@ngrx/router-store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { rootReducer, AppState } from './app.reducer';
import { effects } from './app.effects';

View File

@@ -1,11 +1,13 @@
import { combineReducers, ActionReducer } from "@ngrx/store";
import { routerReducer, RouterState } from "@ngrx/router-store";
import { headerReducer, HeaderState } from './header/header.reducer';
import { hostWindowReducer, HostWindowState } from "./shared/host-window.reducer";
import { CoreState, coreReducer } from "./core/core.reducers";
import { combineReducers, ActionReducer } from '@ngrx/store';
import { routerReducer, RouterState } from '@ngrx/router-store';
import { storeFreeze } from 'ngrx-store-freeze';
import { compose } from "@ngrx/core";
import { StoreActionTypes } from "./store.actions";
import { compose } from '@ngrx/core';
import { headerReducer, HeaderState } from './header/header.reducer';
import { hostWindowReducer, HostWindowState } from './shared/host-window.reducer';
import { CoreState, coreReducer } from './core/core.reducers';
import { StoreActionTypes } from './store.actions';
import { ENV_CONFIG } from '../config';

View File

@@ -4,17 +4,18 @@ import {
} from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Collection } from "../core/shared/collection.model";
import { Bitstream } from "../core/shared/bitstream.model";
import { RemoteData } from "../core/data/remote-data";
import { CollectionDataService } from "../core/data/collection-data.service";
import { Subscription } from "rxjs/Subscription";
import { ItemDataService } from "../core/data/item-data.service";
import { Item } from "../core/shared/item.model";
import { SortOptions, SortDirection } from "../core/cache/models/sort-options.model";
import { PaginationComponentOptions } from "../shared/pagination/pagination-component-options.model";
import { Observable } from "rxjs/Observable";
import { hasValue } from "../shared/empty.util";
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { Collection } from '../core/shared/collection.model';
import { Bitstream } from '../core/shared/bitstream.model';
import { RemoteData } from '../core/data/remote-data';
import { CollectionDataService } from '../core/data/collection-data.service';
import { ItemDataService } from '../core/data/item-data.service';
import { Item } from '../core/shared/item.model';
import { SortOptions, SortDirection } from '../core/cache/models/sort-options.model';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { hasValue } from '../shared/empty.util';
@Component({
selector: 'ds-collection-page',
@@ -37,19 +38,18 @@ export class CollectionPageComponent implements OnInit, OnDestroy {
private ref: ChangeDetectorRef,
private route: ActivatedRoute
) {
this.universalInit();
}
ngOnInit(): void {
this.subs.push(this.route.params.map((params: Params) => params['id'])
this.subs.push(this.route.params.map((params: Params) => params.id)
.subscribe((id: string) => {
this.collectionId = id;
this.collectionData = this.collectionDataService.findById(this.collectionId);
this.subs.push(this.collectionData.payload
.subscribe(collection => this.logoData = collection.logo));
this.subs.push(this.collectionData.payload.subscribe((collection) => this.logoData = collection.logo));
this.config = new PaginationComponentOptions();
this.config.id = "collection-browse";
this.config.id = 'collection-browse';
this.config.pageSizeOptions = [4];
this.config.pageSize = 4;
this.sortConfig = new SortOptions();
@@ -60,12 +60,7 @@ export class CollectionPageComponent implements OnInit, OnDestroy {
}
ngOnDestroy(): void {
this.subs
.filter(sub => hasValue(sub))
.forEach(sub => sub.unsubscribe());
}
universalInit() {
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
}
onPageChange(currentPage: number): void {

View File

@@ -1,6 +1,6 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from "@ngx-translate/core";
import { TranslateModule } from '@ngx-translate/core';
import { SharedModule } from '../shared/shared.module';

View File

@@ -1,12 +1,13 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Community } from "../core/shared/community.model";
import { Bitstream } from "../core/shared/bitstream.model";
import { RemoteData } from "../core/data/remote-data";
import { CommunityDataService } from "../core/data/community-data.service";
import { Subscription } from "rxjs/Subscription";
import { hasValue } from "../shared/empty.util";
import { Subscription } from 'rxjs/Subscription';
import { Community } from '../core/shared/community.model';
import { Bitstream } from '../core/shared/bitstream.model';
import { RemoteData } from '../core/data/remote-data';
import { CommunityDataService } from '../core/data/community-data.service';
import { hasValue } from '../shared/empty.util';
@Component({
selector: 'ds-community-page',
@@ -22,23 +23,21 @@ export class CommunityPageComponent implements OnInit, OnDestroy {
private communityDataService: CommunityDataService,
private route: ActivatedRoute
) {
this.universalInit();
}
ngOnInit(): void {
this.route.params.subscribe((params: Params) => {
this.communityData = this.communityDataService.findById(params['id']);
this.communityData = this.communityDataService.findById(params.id);
this.subs.push(this.communityData.payload
.subscribe(community => this.logoData = community.logo));
.subscribe((community) => this.logoData = community.logo));
});
}
ngOnDestroy(): void {
this.subs
.filter(sub => hasValue(sub))
.forEach(sub => sub.unsubscribe());
.filter((sub) => hasValue(sub))
.forEach((sub) => sub.unsubscribe());
}
universalInit() {
}
}

View File

@@ -1,8 +1,8 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from "@angular/router";
import { RouterModule } from '@angular/router';
import { TranslateModule } from "@ngx-translate/core";
import { TranslateModule } from '@ngx-translate/core';
import { SharedModule } from '../shared/shared.module';
import { CommunityPageComponent } from './community-page.component';

View File

@@ -1,8 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { CollectionDataService } from "../../core/data/collection-data.service";
import { RemoteData } from "../../core/data/remote-data";
import { Collection } from "../../core/shared/collection.model";
import { CollectionDataService } from '../../core/data/collection-data.service';
import { RemoteData } from '../../core/data/remote-data';
import { Collection } from '../../core/shared/collection.model';
@Component({
selector: 'ds-community-page-sub-collection-list',
@@ -12,13 +12,7 @@ import { Collection } from "../../core/shared/collection.model";
export class CommunityPageSubCollectionListComponent implements OnInit {
subCollections: RemoteData<Collection[]>;
constructor(
private cds: CollectionDataService
) {
this.universalInit();
}
universalInit() {
constructor(private cds: CollectionDataService) {
}

View File

@@ -1,28 +1,29 @@
import "reflect-metadata";
import { GenericConstructor } from "../../shared/generic-constructor";
import { CacheableObject } from "../object-cache.reducer";
import { ResourceType } from "../../shared/resource-type";
import 'reflect-metadata';
const mapsToMetadataKey = Symbol("mapsTo");
const relationshipKey = Symbol("relationship");
import { GenericConstructor } from '../../shared/generic-constructor';
import { CacheableObject } from '../object-cache.reducer';
import { ResourceType } from '../../shared/resource-type';
const mapsToMetadataKey = Symbol('mapsTo');
const relationshipKey = Symbol('relationship');
const relationshipMap = new Map();
export function mapsTo(value: GenericConstructor<CacheableObject>) {
return Reflect.metadata(mapsToMetadataKey, value);
};
}
export function getMapsTo(target: any) {
return Reflect.getOwnMetadata(mapsToMetadataKey, target);
};
}
export function relationship(value: ResourceType, isList: boolean = false): any {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
return function r(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
if (!target || !propertyKey) {
return;
}
let metaDataList: Array<string> = relationshipMap.get(target.constructor) || [];
const metaDataList: string[] = relationshipMap.get(target.constructor) || [];
if (metaDataList.indexOf(propertyKey) === -1) {
metaDataList.push(propertyKey);
}
@@ -30,12 +31,12 @@ export function relationship(value: ResourceType, isList: boolean = false): any
return Reflect.metadata(relationshipKey, { resourceType: value, isList }).apply(this, arguments);
};
};
}
export function getRelationMetadata(target: any, propertyKey: string) {
return Reflect.getMetadata(relationshipKey, target, propertyKey);
};
}
export function getRelationships(target: any) {
return relationshipMap.get(target);
};
}

View File

@@ -1,20 +1,21 @@
import { Injectable } from "@angular/core";
import { CacheableObject } from "../object-cache.reducer";
import { ObjectCacheService } from "../object-cache.service";
import { RequestService } from "../../data/request.service";
import { ResponseCacheService } from "../response-cache.service";
import { Store } from "@ngrx/store";
import { CoreState } from "../../core.reducers";
import { RequestEntry } from "../../data/request.reducer";
import { hasValue, isNotEmpty } from "../../../shared/empty.util";
import { ResponseCacheEntry } from "../response-cache.reducer";
import { ErrorResponse, SuccessResponse } from "../response-cache.models";
import { Observable } from "rxjs/Observable";
import { RemoteData } from "../../data/remote-data";
import { GenericConstructor } from "../../shared/generic-constructor";
import { getMapsTo, getRelationMetadata, getRelationships } from "./build-decorators";
import { NormalizedObjectFactory } from "../models/normalized-object-factory";
import { Request } from "../../data/request.models";
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { CacheableObject } from '../object-cache.reducer';
import { ObjectCacheService } from '../object-cache.service';
import { RequestService } from '../../data/request.service';
import { ResponseCacheService } from '../response-cache.service';
import { CoreState } from '../../core.reducers';
import { RequestEntry } from '../../data/request.reducer';
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
import { ResponseCacheEntry } from '../response-cache.reducer';
import { ErrorResponse, SuccessResponse } from '../response-cache.models';
import { RemoteData } from '../../data/remote-data';
import { GenericConstructor } from '../../shared/generic-constructor';
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
import { NormalizedObjectFactory } from '../models/normalized-object-factory';
import { Request } from '../../data/request.models';
@Injectable()
export class RemoteDataBuildService {
@@ -33,14 +34,14 @@ export class RemoteDataBuildService {
const requestHrefObs = this.objectCache.getRequestHrefBySelfLink(href);
const requestObs = Observable.race(
this.store.select<RequestEntry>('core', 'data', 'request', href).filter(entry => hasValue(entry)),
requestHrefObs.flatMap(requestHref =>
this.store.select<RequestEntry>('core', 'data', 'request', requestHref)).filter(entry => hasValue(entry))
this.store.select<RequestEntry>('core', 'data', 'request', href).filter((entry) => hasValue(entry)),
requestHrefObs.flatMap((requestHref) =>
this.store.select<RequestEntry>('core', 'data', 'request', requestHref)).filter((entry) => hasValue(entry))
);
const responseCacheObs = Observable.race(
this.responseCache.get(href).filter(entry => hasValue(entry)),
requestHrefObs.flatMap(requestHref => this.responseCache.get(requestHref)).filter(entry => hasValue(entry))
this.responseCache.get(href).filter((entry) => hasValue(entry)),
requestHrefObs.flatMap((requestHref) => this.responseCache.get(requestHref)).filter((entry) => hasValue(entry))
);
const requestPending = requestObs.map((entry: RequestEntry) => entry.requestPending).distinctUntilChanged();
@@ -52,30 +53,31 @@ export class RemoteDataBuildService {
const errorMessage = responseCacheObs
.filter((entry: ResponseCacheEntry) => !entry.response.isSuccessful)
.map((entry: ResponseCacheEntry) => (<ErrorResponse>entry.response).errorMessage)
.map((entry: ResponseCacheEntry) => (entry.response as ErrorResponse).errorMessage)
.distinctUntilChanged();
const statusCode = responseCacheObs
.map((entry: ResponseCacheEntry) => entry.response.statusCode)
.distinctUntilChanged();
/* tslint:disable:no-string-literal */
const pageInfo = responseCacheObs
.filter((entry: ResponseCacheEntry) => hasValue(entry.response) && hasValue(entry.response['pageInfo']))
.map((entry: ResponseCacheEntry) => (<SuccessResponse>entry.response).pageInfo)
.map((entry: ResponseCacheEntry) => (entry.response as SuccessResponse).pageInfo)
.distinctUntilChanged();
/* tslint:enable:no-string-literal */
//always use self link if that is cached, only if it isn't, get it via the response.
// always use self link if that is cached, only if it isn't, get it via the response.
const payload =
Observable.combineLatest(
this.objectCache.getBySelfLink<TNormalized>(href, normalizedType).startWith(undefined),
responseCacheObs
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
.map((entry: ResponseCacheEntry) => (<SuccessResponse>entry.response).resourceUUIDs)
.flatMap((resourceUUIDs: Array<string>) => {
.map((entry: ResponseCacheEntry) => (entry.response as SuccessResponse).resourceUUIDs)
.flatMap((resourceUUIDs: string[]) => {
if (isNotEmpty(resourceUUIDs)) {
return this.objectCache.get(resourceUUIDs[0], normalizedType);
}
else {
} else {
return Observable.of(undefined);
}
})
@@ -84,17 +86,15 @@ export class RemoteDataBuildService {
(fromSelfLink, fromResponse) => {
if (hasValue(fromSelfLink)) {
return fromSelfLink;
}
else {
} else {
return fromResponse;
}
}
).filter(normalized => hasValue(normalized))
).filter((normalized) => hasValue(normalized))
.map((normalized: TNormalized) => {
return this.build<TNormalized, TDomain>(normalized);
});
return new RemoteData(
href,
requestPending,
@@ -112,8 +112,8 @@ export class RemoteDataBuildService {
normalizedType: GenericConstructor<TNormalized>
): RemoteData<TDomain[]> {
const requestObs = this.store.select<RequestEntry>('core', 'data', 'request', href)
.filter(entry => hasValue(entry));
const responseCacheObs = this.responseCache.get(href).filter(entry => hasValue(entry));
.filter((entry) => hasValue(entry));
const responseCacheObs = this.responseCache.get(href).filter((entry) => hasValue(entry));
const requestPending = requestObs.map((entry: RequestEntry) => entry.requestPending).distinctUntilChanged();
@@ -124,22 +124,24 @@ export class RemoteDataBuildService {
const errorMessage = responseCacheObs
.filter((entry: ResponseCacheEntry) => !entry.response.isSuccessful)
.map((entry: ResponseCacheEntry) => (<ErrorResponse>entry.response).errorMessage)
.map((entry: ResponseCacheEntry) => (entry.response as ErrorResponse).errorMessage)
.distinctUntilChanged();
const statusCode = responseCacheObs
.map((entry: ResponseCacheEntry) => entry.response.statusCode)
.distinctUntilChanged();
/* tslint:disable:no-string-literal */
const pageInfo = responseCacheObs
.filter((entry: ResponseCacheEntry) => hasValue(entry.response) && hasValue(entry.response['pageInfo']))
.map((entry: ResponseCacheEntry) => (<SuccessResponse>entry.response).pageInfo)
.map((entry: ResponseCacheEntry) => (entry.response as SuccessResponse).pageInfo)
.distinctUntilChanged();
/* tslint:enable:no-string-literal */
const payload = responseCacheObs
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
.map((entry: ResponseCacheEntry) => (<SuccessResponse>entry.response).resourceUUIDs)
.flatMap((resourceUUIDs: Array<string>) => {
.map((entry: ResponseCacheEntry) => (entry.response as SuccessResponse).resourceUUIDs)
.flatMap((resourceUUIDs: string[]) => {
return this.objectCache.getList(resourceUUIDs, normalizedType)
.map((normList: TNormalized[]) => {
return normList.map((normalized: TNormalized) => {
@@ -161,9 +163,8 @@ export class RemoteDataBuildService {
);
}
build<TNormalized extends CacheableObject, TDomain>(normalized: TNormalized): TDomain {
let links: any = {};
const links: any = {};
const relationships = getRelationships(normalized.constructor) || [];
@@ -180,19 +181,17 @@ export class RemoteDataBuildService {
});
}, 0);
let rdArr = [];
const rdArr = [];
normalized[relationship].forEach((href: string) => {
rdArr.push(this.buildSingle(href, resourceConstructor));
});
if (rdArr.length === 1) {
links[relationship] = rdArr[0];
}
else {
} else {
links[relationship] = this.aggregate(rdArr);
}
}
else {
} else {
// without the setTimeout, the actions inside requestService.configure
// are dispatched, but sometimes don't arrive. I'm unsure why atm.
setTimeout(() => {
@@ -215,51 +214,49 @@ export class RemoteDataBuildService {
return Object.assign(new domainModel(), normalized, links);
}
aggregate<T>(input: RemoteData<T>[]): RemoteData<T[]> {
aggregate<T>(input: Array<RemoteData<T>>): RemoteData<T[]> {
const requestPending = Observable.combineLatest(
...input.map(rd => rd.isRequestPending),
).map((...pendingArray) => pendingArray.every(e => e === true))
...input.map((rd) => rd.isRequestPending),
).map((...pendingArray) => pendingArray.every((e) => e === true))
.distinctUntilChanged();
const responsePending = Observable.combineLatest(
...input.map(rd => rd.isResponsePending),
).map((...pendingArray) => pendingArray.every(e => e === true))
...input.map((rd) => rd.isResponsePending),
).map((...pendingArray) => pendingArray.every((e) => e === true))
.distinctUntilChanged();
const isSuccessFul = Observable.combineLatest(
...input.map(rd => rd.hasSucceeded),
).map((...successArray) => successArray.every(e => e === true))
...input.map((rd) => rd.hasSucceeded),
).map((...successArray) => successArray.every((e) => e === true))
.distinctUntilChanged();
const errorMessage = Observable.combineLatest(
...input.map(rd => rd.errorMessage),
...input.map((rd) => rd.errorMessage),
).map((...errors) => errors
.map((e, idx) => {
if (hasValue(e)) {
return `[${idx}]: ${e}`;
}
})
.filter(e => hasValue(e))
.join(", ")
.filter((e) => hasValue(e))
.join(', ')
);
const statusCode = Observable.combineLatest(
...input.map(rd => rd.statusCode),
...input.map((rd) => rd.statusCode),
).map((...statusCodes) => statusCodes
.map((code, idx) => {
if (hasValue(code)) {
return `[${idx}]: ${code}`;
}
})
.filter(c => hasValue(c))
.join(", ")
.filter((c) => hasValue(c))
.join(', ')
);
const pageInfo = Observable.of(undefined);
const payload = <Observable<T[]>>Observable.combineLatest(
...input.map(rd => rd.payload)
);
const payload = Observable.combineLatest(...input.map((rd) => rd.payload)) as Observable<T[]>;
return new RemoteData(
// This is an aggregated object, it doesn't necessarily correspond
@@ -275,4 +272,5 @@ export class RemoteDataBuildService {
payload
);
}
}

View File

@@ -1,6 +1,7 @@
import { combineReducers } from "@ngrx/store";
import { ResponseCacheState, responseCacheReducer } from "./response-cache.reducer";
import { ObjectCacheState, objectCacheReducer } from "./object-cache.reducer";
import { combineReducers } from '@ngrx/store';
import { ResponseCacheState, responseCacheReducer } from './response-cache.reducer';
import { ObjectCacheState, objectCacheReducer } from './object-cache.reducer';
export interface CacheState {
response: ResponseCacheState,

View File

@@ -1,11 +1,13 @@
import { NormalizedObject } from "./normalized-object.model";
import { inheritSerialization } from "cerialize";
import { inheritSerialization } from 'cerialize';
import { NormalizedObject } from './normalized-object.model';
@inheritSerialization(NormalizedObject)
export class NormalizedBitstreamFormat extends NormalizedObject {
//TODO this class was created as a placeholder when we connected to the live rest api
// TODO: this class was created as a placeholder when we connected to the live rest api
get uuid(): string {
return this.self;
}
}

View File

@@ -1,8 +1,9 @@
import { inheritSerialization, autoserialize } from "cerialize";
import { NormalizedDSpaceObject } from "./normalized-dspace-object.model";
import { Bitstream } from "../../shared/bitstream.model";
import { mapsTo, relationship } from "../builders/build-decorators";
import { ResourceType } from "../../shared/resource-type";
import { inheritSerialization, autoserialize } from 'cerialize';
import { NormalizedDSpaceObject } from './normalized-dspace-object.model';
import { Bitstream } from '../../shared/bitstream.model';
import { mapsTo, relationship } from '../builders/build-decorators';
import { ResourceType } from '../../shared/resource-type';
@mapsTo(Bitstream)
@inheritSerialization(NormalizedDSpaceObject)
@@ -43,7 +44,7 @@ export class NormalizedBitstream extends NormalizedDSpaceObject {
*/
@autoserialize
@relationship(ResourceType.Item, true)
parents: Array<string>;
parents: string[];
/**
* The Bundle that owns this Bitstream
@@ -57,4 +58,5 @@ export class NormalizedBitstream extends NormalizedDSpaceObject {
*/
@autoserialize
bundleName: string;
}

View File

@@ -1,8 +1,9 @@
import { autoserialize, inheritSerialization } from "cerialize";
import { NormalizedDSpaceObject } from "./normalized-dspace-object.model";
import { Bundle } from "../../shared/bundle.model";
import { mapsTo, relationship } from "../builders/build-decorators";
import { ResourceType } from "../../shared/resource-type";
import { autoserialize, inheritSerialization } from 'cerialize';
import { NormalizedDSpaceObject } from './normalized-dspace-object.model';
import { Bundle } from '../../shared/bundle.model';
import { mapsTo, relationship } from '../builders/build-decorators';
import { ResourceType } from '../../shared/resource-type';
@mapsTo(Bundle)
@inheritSerialization(NormalizedDSpaceObject)
@@ -17,7 +18,7 @@ export class NormalizedBundle extends NormalizedDSpaceObject {
/**
* An array of Items that are direct parents of this Bundle
*/
parents: Array<string>;
parents: string[];
/**
* The Item that owns this Bundle
@@ -26,5 +27,6 @@ export class NormalizedBundle extends NormalizedDSpaceObject {
@autoserialize
@relationship(ResourceType.Bitstream, true)
bitstreams: Array<string>;
bitstreams: string[];
}

View File

@@ -1,8 +1,9 @@
import { autoserialize, inheritSerialization, autoserializeAs } from "cerialize";
import { NormalizedDSpaceObject } from "./normalized-dspace-object.model";
import { Collection } from "../../shared/collection.model";
import { mapsTo, relationship } from "../builders/build-decorators";
import { ResourceType } from "../../shared/resource-type";
import { autoserialize, inheritSerialization, autoserializeAs } from 'cerialize';
import { NormalizedDSpaceObject } from './normalized-dspace-object.model';
import { Collection } from '../../shared/collection.model';
import { mapsTo, relationship } from '../builders/build-decorators';
import { ResourceType } from '../../shared/resource-type';
@mapsTo(Collection)
@inheritSerialization(NormalizedDSpaceObject)
@@ -26,7 +27,7 @@ export class NormalizedCollection extends NormalizedDSpaceObject {
*/
@autoserialize
@relationship(ResourceType.Community, true)
parents: Array<string>;
parents: string[];
/**
* The Community that owns this Collection
@@ -37,6 +38,6 @@ export class NormalizedCollection extends NormalizedDSpaceObject {
@autoserialize
@relationship(ResourceType.Item, true)
items: Array<string>;
items: string[];
}

View File

@@ -1,8 +1,9 @@
import { autoserialize, inheritSerialization, autoserializeAs } from "cerialize";
import { NormalizedDSpaceObject } from "./normalized-dspace-object.model";
import { Community } from "../../shared/community.model";
import { mapsTo, relationship } from "../builders/build-decorators";
import { ResourceType } from "../../shared/resource-type";
import { autoserialize, inheritSerialization, autoserializeAs } from 'cerialize';
import { NormalizedDSpaceObject } from './normalized-dspace-object.model';
import { Community } from '../../shared/community.model';
import { mapsTo, relationship } from '../builders/build-decorators';
import { ResourceType } from '../../shared/resource-type';
@mapsTo(Community)
@inheritSerialization(NormalizedDSpaceObject)
@@ -26,7 +27,7 @@ export class NormalizedCommunity extends NormalizedDSpaceObject {
*/
@autoserialize
@relationship(ResourceType.Community, true)
parents: Array<string>;
parents: string[];
/**
* The Community that owns this Community
@@ -37,6 +38,6 @@ export class NormalizedCommunity extends NormalizedDSpaceObject {
@autoserialize
@relationship(ResourceType.Collection, true)
collections: Array<string>;
collections: string[];
}

View File

@@ -1,7 +1,8 @@
import { autoserialize, autoserializeAs, inheritSerialization } from "cerialize";
import { Metadatum } from "../../shared/metadatum.model";
import { ResourceType } from "../../shared/resource-type";
import { NormalizedObject } from "./normalized-object.model";
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
import { Metadatum } from '../../shared/metadatum.model';
import { ResourceType } from '../../shared/resource-type';
import { NormalizedObject } from './normalized-object.model';
/**
* An abstract model class for a DSpaceObject.
@@ -51,17 +52,18 @@ export abstract class NormalizedDSpaceObject extends NormalizedObject {
* An array containing all metadata of this DSpaceObject
*/
@autoserializeAs(Metadatum)
metadata: Array<Metadatum>;
metadata: Metadatum[];
/**
* An array of DSpaceObjects that are direct parents of this DSpaceObject
*/
@autoserialize
parents: Array<string>;
parents: string[];
/**
* The DSpaceObject that owns this DSpaceObject
*/
@autoserialize
owner: string;
}

View File

@@ -1,8 +1,9 @@
import { inheritSerialization, autoserialize, autoserializeAs } from "cerialize";
import { NormalizedDSpaceObject } from "./normalized-dspace-object.model";
import { Item } from "../../shared/item.model";
import { mapsTo, relationship } from "../builders/build-decorators";
import { ResourceType } from "../../shared/resource-type";
import { inheritSerialization, autoserialize, autoserializeAs } from 'cerialize';
import { NormalizedDSpaceObject } from './normalized-dspace-object.model';
import { Item } from '../../shared/item.model';
import { mapsTo, relationship } from '../builders/build-decorators';
import { ResourceType } from '../../shared/resource-type';
@mapsTo(Item)
@inheritSerialization(NormalizedDSpaceObject)
@@ -43,7 +44,7 @@ export class NormalizedItem extends NormalizedDSpaceObject {
*/
@autoserialize
@relationship(ResourceType.Collection, true)
parents: Array<string>;
parents: string[];
/**
* The Collection that owns this Item
@@ -53,5 +54,6 @@ export class NormalizedItem extends NormalizedDSpaceObject {
@autoserialize
@relationship(ResourceType.Bitstream, true)
bitstreams: Array<string>;
bitstreams: string[];
}

View File

@@ -1,13 +1,13 @@
import { NormalizedDSpaceObject } from "./normalized-dspace-object.model";
import { NormalizedBitstream } from "./normalized-bitstream.model";
import { NormalizedBundle } from "./normalized-bundle.model";
import { NormalizedItem } from "./normalized-item.model";
import { NormalizedCollection } from "./normalized-collection.model";
import { GenericConstructor } from "../../shared/generic-constructor";
import { NormalizedCommunity } from "./normalized-community.model";
import { ResourceType } from "../../shared/resource-type";
import { NormalizedObject } from "./normalized-object.model";
import { NormalizedBitstreamFormat } from "./normalized-bitstream-format.model";
import { NormalizedDSpaceObject } from './normalized-dspace-object.model';
import { NormalizedBitstream } from './normalized-bitstream.model';
import { NormalizedBundle } from './normalized-bundle.model';
import { NormalizedItem } from './normalized-item.model';
import { NormalizedCollection } from './normalized-collection.model';
import { GenericConstructor } from '../../shared/generic-constructor';
import { NormalizedCommunity } from './normalized-community.model';
import { ResourceType } from '../../shared/resource-type';
import { NormalizedObject } from './normalized-object.model';
import { NormalizedBitstreamFormat } from './normalized-bitstream-format.model';
export class NormalizedObjectFactory {
public static getConstructor(type: ResourceType): GenericConstructor<NormalizedObject> {

View File

@@ -1,5 +1,5 @@
import { CacheableObject } from "../object-cache.reducer";
import { autoserialize } from "cerialize";
import { CacheableObject } from '../object-cache.reducer';
import { autoserialize } from 'cerialize';
/**
* An abstract model class for a NormalizedObject.
*/

View File

@@ -1,4 +1,4 @@
import { autoserialize } from "cerialize";
import { autoserialize } from 'cerialize';
export class SelfLink {
@@ -7,4 +7,5 @@ export class SelfLink {
@autoserialize
uuid: string;
}

View File

@@ -5,7 +5,7 @@ export enum SortDirection {
export class SortOptions {
constructor(public field: string = "name", public direction: SortDirection = SortDirection.Ascending) {
constructor(public field: string = 'name', public direction: SortDirection = SortDirection.Ascending) {
}
}

View File

@@ -1,6 +1,7 @@
import { Action } from "@ngrx/store";
import { type } from "../../shared/ngrx/type";
import { CacheableObject } from "./object-cache.reducer";
import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type';
import { CacheableObject } from './object-cache.reducer';
/**
* The list of ObjectCacheAction type definitions
@@ -11,6 +12,7 @@ export const ObjectCacheActionTypes = {
RESET_TIMESTAMPS: type('dspace/core/cache/object/RESET_TIMESTAMPS')
};
/* tslint:disable:max-classes-per-file */
/**
* An ngrx action to add an object to the cache
*/
@@ -77,6 +79,7 @@ export class ResetObjectCacheTimestampsAction implements Action {
this.payload = newTimestamp;
}
}
/* tslint:enable:max-classes-per-file */
/**
* A type to encompass all ObjectCacheActions

View File

@@ -1,9 +1,10 @@
import * as deepFreeze from "deep-freeze";
import { objectCacheReducer } from "./object-cache.reducer";
import * as deepFreeze from 'deep-freeze';
import { objectCacheReducer } from './object-cache.reducer';
import {
AddToObjectCacheAction,
RemoveFromObjectCacheAction, ResetObjectCacheTimestampsAction
} from "./object-cache.actions";
} from './object-cache.actions';
class NullAction extends RemoveFromObjectCacheAction {
type = null;
@@ -14,51 +15,51 @@ class NullAction extends RemoveFromObjectCacheAction {
}
}
describe("objectCacheReducer", () => {
describe('objectCacheReducer', () => {
const uuid1 = '1698f1d3-be98-4c51-9fd8-6bfedcbd59b7';
const uuid2 = '28b04544-1766-4e82-9728-c4e93544ecd3';
const testState = {
[uuid1]: {
data: {
uuid: uuid1,
foo: "bar"
foo: 'bar'
},
timeAdded: new Date().getTime(),
msToLive: 900000,
requestHref: "https://rest.api/endpoint/uuid1"
requestHref: 'https://rest.api/endpoint/uuid1'
},
[uuid2]: {
data: {
uuid: uuid2,
foo: "baz"
foo: 'baz'
},
timeAdded: new Date().getTime(),
msToLive: 900000,
requestHref: "https://rest.api/endpoint/uuid2"
requestHref: 'https://rest.api/endpoint/uuid2'
}
};
deepFreeze(testState);
it("should return the current state when no valid actions have been made", () => {
it('should return the current state when no valid actions have been made', () => {
const action = new NullAction();
const newState = objectCacheReducer(testState, action);
expect(newState).toEqual(testState);
});
it("should start with an empty cache", () => {
it('should start with an empty cache', () => {
const action = new NullAction();
const initialState = objectCacheReducer(undefined, action);
expect(initialState).toEqual(Object.create(null));
});
it("should add the payload to the cache in response to an ADD action", () => {
it('should add the payload to the cache in response to an ADD action', () => {
const state = Object.create(null);
const objectToCache = { uuid: uuid1 };
const timeAdded = new Date().getTime();
const msToLive = 900000;
const requestHref = "https://rest.api/endpoint/uuid1";
const requestHref = 'https://rest.api/endpoint/uuid1';
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive, requestHref);
const newState = objectCacheReducer(state, action);
@@ -67,31 +68,33 @@ describe("objectCacheReducer", () => {
expect(newState[uuid1].msToLive).toEqual(msToLive);
});
it("should overwrite an object in the cache in response to an ADD action if it already exists", () => {
const objectToCache = { uuid: uuid1, foo: "baz", somethingElse: true };
it('should overwrite an object in the cache in response to an ADD action if it already exists', () => {
const objectToCache = { uuid: uuid1, foo: 'baz', somethingElse: true };
const timeAdded = new Date().getTime();
const msToLive = 900000;
const requestHref = "https://rest.api/endpoint/uuid1";
const requestHref = 'https://rest.api/endpoint/uuid1';
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive, requestHref);
const newState = objectCacheReducer(testState, action);
expect(newState[uuid1].data['foo']).toBe("baz");
/* tslint:disable:no-string-literal */
expect(newState[uuid1].data['foo']).toBe('baz');
expect(newState[uuid1].data['somethingElse']).toBe(true);
/* tslint:enable:no-string-literal */
});
it("should perform the ADD action without affecting the previous state", () => {
it('should perform the ADD action without affecting the previous state', () => {
const state = Object.create(null);
const objectToCache = { uuid: uuid1 };
const timeAdded = new Date().getTime();
const msToLive = 900000;
const requestHref = "https://rest.api/endpoint/uuid1";
const requestHref = 'https://rest.api/endpoint/uuid1';
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive, requestHref);
deepFreeze(state);
objectCacheReducer(state, action);
});
it("should remove the specified object from the cache in response to the REMOVE action", () => {
it('should remove the specified object from the cache in response to the REMOVE action', () => {
const action = new RemoveFromObjectCacheAction(uuid1);
const newState = objectCacheReducer(testState, action);
@@ -108,13 +111,13 @@ describe("objectCacheReducer", () => {
expect(newState).toEqual(testState);
});
it("should perform the REMOVE action without affecting the previous state", () => {
it('should perform the REMOVE action without affecting the previous state', () => {
const action = new RemoveFromObjectCacheAction(uuid1);
//testState has already been frozen above
// testState has already been frozen above
objectCacheReducer(testState, action);
});
it("should set the timestamp of all objects in the cache in response to a RESET_TIMESTAMPS action", () => {
it('should set the timestamp of all objects in the cache in response to a RESET_TIMESTAMPS action', () => {
const newTimestamp = new Date().getTime();
const action = new ResetObjectCacheTimestampsAction(newTimestamp);
const newState = objectCacheReducer(testState, action);
@@ -123,9 +126,9 @@ describe("objectCacheReducer", () => {
});
});
it("should perform the RESET_TIMESTAMPS action without affecting the previous state", () => {
it('should perform the RESET_TIMESTAMPS action without affecting the previous state', () => {
const action = new ResetObjectCacheTimestampsAction(new Date().getTime());
//testState has already been frozen above
// testState has already been frozen above
objectCacheReducer(testState, action);
});

View File

@@ -1,9 +1,9 @@
import {
ObjectCacheAction, ObjectCacheActionTypes, AddToObjectCacheAction,
RemoveFromObjectCacheAction, ResetObjectCacheTimestampsAction
} from "./object-cache.actions";
import { hasValue } from "../../shared/empty.util";
import { CacheEntry } from "./cache-entry";
} from './object-cache.actions';
import { hasValue } from '../../shared/empty.util';
import { CacheEntry } from './cache-entry';
/**
* An interface to represent objects that can be cached
@@ -52,15 +52,15 @@ export const objectCacheReducer = (state = initialState, action: ObjectCacheActi
switch (action.type) {
case ObjectCacheActionTypes.ADD: {
return addToObjectCache(state, <AddToObjectCacheAction>action);
return addToObjectCache(state, action as AddToObjectCacheAction);
}
case ObjectCacheActionTypes.REMOVE: {
return removeFromObjectCache(state, <RemoveFromObjectCacheAction>action)
return removeFromObjectCache(state, action as RemoveFromObjectCacheAction)
}
case ObjectCacheActionTypes.RESET_TIMESTAMPS: {
return resetObjectCacheTimestamps(state, <ResetObjectCacheTimestampsAction>action)
return resetObjectCacheTimestamps(state, action as ResetObjectCacheTimestampsAction)
}
default: {
@@ -102,12 +102,11 @@ function addToObjectCache(state: ObjectCacheState, action: AddToObjectCacheActio
*/
function removeFromObjectCache(state: ObjectCacheState, action: RemoveFromObjectCacheAction): ObjectCacheState {
if (hasValue(state[action.payload])) {
let newObjectCache = Object.assign({}, state);
const newObjectCache = Object.assign({}, state);
delete newObjectCache[action.payload];
return newObjectCache;
}
else {
} else {
return state;
}
}
@@ -123,8 +122,8 @@ function removeFromObjectCache(state: ObjectCacheState, action: RemoveFromObject
* the new state, with all timeAdded timestamps set to the specified value
*/
function resetObjectCacheTimestamps(state: ObjectCacheState, action: ResetObjectCacheTimestampsAction): ObjectCacheState {
let newState = Object.create(null);
Object.keys(state).forEach(key => {
const newState = Object.create(null);
Object.keys(state).forEach((key) => {
newState[key] = Object.assign({}, state[key], {
timeAdded: action.payload
});

View File

@@ -1,9 +1,9 @@
import { Store } from "@ngrx/store";
import { Observable } from "rxjs";
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { ObjectCacheService } from "./object-cache.service";
import { ObjectCacheState, CacheableObject } from "./object-cache.reducer";
import { AddToObjectCacheAction, RemoveFromObjectCacheAction } from "./object-cache.actions";
import { ObjectCacheService } from './object-cache.service';
import { ObjectCacheState, CacheableObject } from './object-cache.reducer';
import { AddToObjectCacheAction, RemoveFromObjectCacheAction } from './object-cache.actions';
class TestClass implements CacheableObject {
constructor(
@@ -16,7 +16,7 @@ class TestClass implements CacheableObject {
}
}
describe("ObjectCacheService", () => {
describe('ObjectCacheService', () => {
let service: ObjectCacheService;
let store: Store<ObjectCacheState>;
@@ -40,65 +40,65 @@ describe("ObjectCacheService", () => {
spyOn(store, 'dispatch');
service = new ObjectCacheService(store);
spyOn(Date.prototype, 'getTime').and.callFake(function() {
spyOn(Date.prototype, 'getTime').and.callFake(() => {
return timestamp;
});
});
describe("add", () => {
it("should dispatch an ADD action with the object to add, the time to live, and the current timestamp", () => {
describe('add', () => {
it('should dispatch an ADD action with the object to add, the time to live, and the current timestamp', () => {
service.add(objectToCache, msToLive, requestHref);
expect(store.dispatch).toHaveBeenCalledWith(new AddToObjectCacheAction(objectToCache, timestamp, msToLive, requestHref));
});
});
describe("remove", () => {
it("should dispatch a REMOVE action with the UUID of the object to remove", () => {
describe('remove', () => {
it('should dispatch a REMOVE action with the UUID of the object to remove', () => {
service.remove(uuid);
expect(store.dispatch).toHaveBeenCalledWith(new RemoveFromObjectCacheAction(uuid));
});
});
describe("get", () => {
it("should return an observable of the cached object with the specified UUID and type", () => {
describe('get', () => {
it('should return an observable of the cached object with the specified UUID and type', () => {
spyOn(store, 'select').and.returnValue(Observable.of(cacheEntry));
let testObj: any;
//due to the implementation of spyOn above, this subscribe will be synchronous
service.get(uuid, TestClass).take(1).subscribe(o => testObj = o);
// due to the implementation of spyOn above, this subscribe will be synchronous
service.get(uuid, TestClass).take(1).subscribe((o) => testObj = o);
expect(testObj.uuid).toBe(uuid);
expect(testObj.foo).toBe("bar");
expect(testObj.foo).toBe('bar');
// this only works if testObj is an instance of TestClass
expect(testObj.test()).toBe("bar" + uuid);
expect(testObj.test()).toBe('bar' + uuid);
});
it("should not return a cached object that has exceeded its time to live", () => {
it('should not return a cached object that has exceeded its time to live', () => {
spyOn(store, 'select').and.returnValue(Observable.of(invalidCacheEntry));
let getObsHasFired = false;
const subscription = service.get(uuid, TestClass).subscribe(o => getObsHasFired = true);
const subscription = service.get(uuid, TestClass).subscribe((o) => getObsHasFired = true);
expect(getObsHasFired).toBe(false);
subscription.unsubscribe();
});
});
describe("getList", () => {
it("should return an observable of the array of cached objects with the specified UUID and type", () => {
spyOn(service, 'get').and.returnValue(Observable.of(new TestClass(uuid, "bar")));
describe('getList', () => {
it('should return an observable of the array of cached objects with the specified UUID and type', () => {
spyOn(service, 'get').and.returnValue(Observable.of(new TestClass(uuid, 'bar')));
let testObjs: Array<any>;
service.getList([uuid, uuid], TestClass).take(1).subscribe(arr => testObjs = arr);
let testObjs: any[];
service.getList([uuid, uuid], TestClass).take(1).subscribe((arr) => testObjs = arr);
expect(testObjs[0].uuid).toBe(uuid);
expect(testObjs[0].foo).toBe("bar");
expect(testObjs[0].test()).toBe("bar" + uuid);
expect(testObjs[0].foo).toBe('bar');
expect(testObjs[0].test()).toBe('bar' + uuid);
expect(testObjs[1].uuid).toBe(uuid);
expect(testObjs[1].foo).toBe("bar");
expect(testObjs[1].test()).toBe("bar" + uuid);
expect(testObjs[1].foo).toBe('bar');
expect(testObjs[1].test()).toBe('bar' + uuid);
});
});
describe("has", () => {
it("should return true if the object with the supplied UUID is cached and still valid", () => {
describe('has', () => {
it('should return true if the object with the supplied UUID is cached and still valid', () => {
spyOn(store, 'select').and.returnValue(Observable.of(cacheEntry));
expect(service.has(uuid)).toBe(true);
@@ -110,10 +110,11 @@ describe("ObjectCacheService", () => {
expect(service.has(uuid)).toBe(false);
});
it("should return false if the object with the supplied UUID is cached but has exceeded its time to live", () => {
it('should return false if the object with the supplied UUID is cached but has exceeded its time to live', () => {
spyOn(store, 'select').and.returnValue(Observable.of(invalidCacheEntry));
expect(service.has(uuid)).toBe(false);
});
});
});

View File

@@ -1,10 +1,12 @@
import { Injectable } from "@angular/core";
import { Store } from "@ngrx/store";
import { ObjectCacheState, ObjectCacheEntry, CacheableObject } from "./object-cache.reducer";
import { AddToObjectCacheAction, RemoveFromObjectCacheAction } from "./object-cache.actions";
import { Observable } from "rxjs";
import { hasNoValue } from "../../shared/empty.util";
import { GenericConstructor } from "../shared/generic-constructor";
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { ObjectCacheState, ObjectCacheEntry, CacheableObject } from './object-cache.reducer';
import { AddToObjectCacheAction, RemoveFromObjectCacheAction } from './object-cache.actions';
import { hasNoValue } from '../../shared/empty.util';
import { GenericConstructor } from '../shared/generic-constructor';
/**
* A service to interact with the object cache
@@ -59,7 +61,7 @@ export class ObjectCacheService {
*/
get<T extends CacheableObject>(uuid: string, type: GenericConstructor<T>): Observable<T> {
return this.getEntry(uuid)
.map((entry: ObjectCacheEntry) => <T>Object.assign(new type(), entry.data));
.map((entry: ObjectCacheEntry) => Object.assign(new type(), entry.data) as T);
}
getBySelfLink<T extends CacheableObject>(href: string, type: GenericConstructor<T>): Observable<T> {
@@ -69,7 +71,7 @@ export class ObjectCacheService {
private getEntry(uuid: string): Observable<ObjectCacheEntry> {
return this.store.select<ObjectCacheEntry>('core', 'cache', 'object', uuid)
.filter(entry => this.isValid(entry))
.filter((entry) => this.isValid(entry))
.distinctUntilChanged();
}
@@ -103,7 +105,7 @@ export class ObjectCacheService {
* The type of the objects to get
* @return Observable<Array<T>>
*/
getList<T extends CacheableObject>(uuids: Array<string>, type: GenericConstructor<T>): Observable<Array<T>> {
getList<T extends CacheableObject>(uuids: string[], type: GenericConstructor<T>): Observable<T[]> {
return Observable.combineLatest(
uuids.map((id: string) => this.get<T>(id, type))
);
@@ -123,7 +125,7 @@ export class ObjectCacheService {
this.store.select<ObjectCacheEntry>('core', 'cache', 'object', uuid)
.take(1)
.subscribe(entry => result = this.isValid(entry));
.subscribe((entry) => result = this.isValid(entry));
return result;
}
@@ -138,7 +140,7 @@ export class ObjectCacheService {
* false otherwise
*/
hasBySelfLink(href: string): boolean {
let result: boolean = false;
let result = false;
this.store.select<string>('core', 'index', 'href', href)
.take(1)
@@ -159,8 +161,7 @@ export class ObjectCacheService {
private isValid(entry: ObjectCacheEntry): boolean {
if (hasNoValue(entry)) {
return false;
}
else {
} else {
const timeOutdated = entry.timeAdded + entry.msToLive;
const isOutDated = new Date().getTime() > timeOutdated;
if (isOutDated) {

View File

@@ -1,6 +1,7 @@
import { Action } from "@ngrx/store";
import { type } from "../../shared/ngrx/type";
import { Response } from "./response-cache.models";
import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type';
import { Response } from './response-cache.models';
/**
* The list of ResponseCacheAction type definitions
@@ -11,6 +12,7 @@ export const ResponseCacheActionTypes = {
RESET_TIMESTAMPS: type('dspace/core/cache/response/RESET_TIMESTAMPS')
};
/* tslint:disable:max-classes-per-file */
export class ResponseCacheAddAction implements Action {
type = ResponseCacheActionTypes.ADD;
payload: {
@@ -59,6 +61,7 @@ export class ResetResponseCacheTimestampsAction implements Action {
this.payload = newTimestamp;
}
}
/* tslint:enable:max-classes-per-file */
/**
* A type to encompass all ResponseCacheActions

View File

@@ -1,6 +1,7 @@
import { RequestError } from "../data/request.models";
import { PageInfo } from "../shared/page-info.model";
import { RequestError } from '../data/request.models';
import { PageInfo } from '../shared/page-info.model';
/* tslint:disable:max-classes-per-file */
export class Response {
constructor(
public isSuccessful: boolean,
@@ -10,7 +11,7 @@ export class Response {
export class SuccessResponse extends Response {
constructor(
public resourceUUIDs: Array<String>,
public resourceUUIDs: string[],
public statusCode: string,
public pageInfo?: PageInfo
) {
@@ -27,3 +28,4 @@ export class ErrorResponse extends Response {
this.errorMessage = error.message;
}
}
/* tslint:enable:max-classes-per-file */

View File

@@ -1,11 +1,11 @@
import * as deepFreeze from "deep-freeze";
import * as deepFreeze from 'deep-freeze';
import { responseCacheReducer, ResponseCacheState } from "./response-cache.reducer";
import { responseCacheReducer, ResponseCacheState } from './response-cache.reducer';
import {
ResponseCacheRemoveAction,
ResetResponseCacheTimestampsAction
} from "./response-cache.actions";
} from './response-cache.actions';
class NullAction extends ResponseCacheRemoveAction {
type = null;
@@ -16,37 +16,37 @@ class NullAction extends ResponseCacheRemoveAction {
}
}
// describe("responseCacheReducer", () => {
// const keys = ["125c17f89046283c5f0640722aac9feb", "a06c3006a41caec5d635af099b0c780c"];
// describe('responseCacheReducer', () => {
// const keys = ['125c17f89046283c5f0640722aac9feb', 'a06c3006a41caec5d635af099b0c780c'];
// const services = [new OpaqueToken('service1'), new OpaqueToken('service2')];
// const msToLive = 900000;
// const uuids = [
// "9e32a2e2-6b91-4236-a361-995ccdc14c60",
// "598ce822-c357-46f3-ab70-63724d02d6ad",
// "be8325f7-243b-49f4-8a4b-df2b793ff3b5"
// '9e32a2e2-6b91-4236-a361-995ccdc14c60',
// '598ce822-c357-46f3-ab70-63724d02d6ad',
// 'be8325f7-243b-49f4-8a4b-df2b793ff3b5'
// ];
// const resourceID = "9978";
// const paginationOptions = { "resultsPerPage": 10, "currentPage": 1 };
// const sortOptions = { "field": "id", "direction": 0 };
// const resourceID = '9978';
// const paginationOptions = { 'resultsPerPage': 10, 'currentPage': 1 };
// const sortOptions = { 'field': 'id', 'direction': 0 };
// const testState = {
// [keys[0]]: {
// "key": keys[0],
// "service": services[0],
// "resourceUUIDs": [uuids[0], uuids[1]],
// "isLoading": false,
// "paginationOptions": paginationOptions,
// "sortOptions": sortOptions,
// "timeAdded": new Date().getTime(),
// "msToLive": msToLive
// 'key': keys[0],
// 'service': services[0],
// 'resourceUUIDs': [uuids[0], uuids[1]],
// 'isLoading': false,
// 'paginationOptions': paginationOptions,
// 'sortOptions': sortOptions,
// 'timeAdded': new Date().getTime(),
// 'msToLive': msToLive
// },
// [keys[1]]: {
// "key": keys[1],
// "service": services[1],
// "resourceID": resourceID,
// "resourceUUIDs": [uuids[2]],
// "isLoading": false,
// "timeAdded": new Date().getTime(),
// "msToLive": msToLive
// 'key': keys[1],
// 'service': services[1],
// 'resourceID': resourceID,
// 'resourceUUIDs': [uuids[2]],
// 'isLoading': false,
// 'timeAdded': new Date().getTime(),
// 'msToLive': msToLive
// }
// };
// deepFreeze(testState);
@@ -59,29 +59,29 @@ class NullAction extends ResponseCacheRemoveAction {
// deepFreeze(errorState);
//
//
// it("should return the current state when no valid actions have been made", () => {
// it('should return the current state when no valid actions have been made', () => {
// const action = new NullAction();
// const newState = responseCacheReducer(testState, action);
//
// expect(newState).toEqual(testState);
// });
//
// it("should start with an empty cache", () => {
// it('should start with an empty cache', () => {
// const action = new NullAction();
// const initialState = responseCacheReducer(undefined, action);
//
// expect(initialState).toEqual(Object.create(null));
// });
//
// describe("FIND_BY_ID", () => {
// describe('FIND_BY_ID', () => {
// const action = new ResponseCacheFindByIDAction(keys[0], services[0], resourceID);
//
// it("should perform the action without affecting the previous state", () => {
// it('should perform the action without affecting the previous state', () => {
// //testState has already been frozen above
// responseCacheReducer(testState, action);
// });
//
// it("should add the request to the cache", () => {
// it('should add the request to the cache', () => {
// const state = Object.create(null);
// const newState = responseCacheReducer(state, action);
// expect(newState[keys[0]].key).toBe(keys[0]);
@@ -89,28 +89,28 @@ class NullAction extends ResponseCacheRemoveAction {
// expect(newState[keys[0]].resourceID).toBe(resourceID);
// });
//
// it("should set responsePending to true", () => {
// it('should set responsePending to true', () => {
// const state = Object.create(null);
// const newState = responseCacheReducer(state, action);
// expect(newState[keys[0]].responsePending).toBe(true);
// });
//
// it("should remove any previous error message or resourceUUID for the request", () => {
// it('should remove any previous error message or resourceUUID for the request', () => {
// const newState = responseCacheReducer(errorState, action);
// expect(newState[keys[0]].resourceUUIDs.length).toBe(0);
// expect(newState[keys[0]].errorMessage).toBeUndefined();
// });
// });
//
// describe("FIND_ALL", () => {
// describe('FIND_ALL', () => {
// const action = new ResponseCacheFindAllAction(keys[0], services[0], resourceID, paginationOptions, sortOptions);
//
// it("should perform the action without affecting the previous state", () => {
// it('should perform the action without affecting the previous state', () => {
// //testState has already been frozen above
// responseCacheReducer(testState, action);
// });
//
// it("should add the request to the cache", () => {
// it('should add the request to the cache', () => {
// const state = Object.create(null);
// const newState = responseCacheReducer(state, action);
// expect(newState[keys[0]].key).toBe(keys[0]);
@@ -120,84 +120,84 @@ class NullAction extends ResponseCacheRemoveAction {
// expect(newState[keys[0]].sortOptions).toEqual(sortOptions);
// });
//
// it("should set responsePending to true", () => {
// it('should set responsePending to true', () => {
// const state = Object.create(null);
// const newState = responseCacheReducer(state, action);
// expect(newState[keys[0]].responsePending).toBe(true);
// });
//
// it("should remove any previous error message or resourceUUIDs for the request", () => {
// it('should remove any previous error message or resourceUUIDs for the request', () => {
// const newState = responseCacheReducer(errorState, action);
// expect(newState[keys[0]].resourceUUIDs.length).toBe(0);
// expect(newState[keys[0]].errorMessage).toBeUndefined();
// });
// });
//
// describe("SUCCESS", () => {
// describe('SUCCESS', () => {
// const successUUIDs = [uuids[0], uuids[2]];
// const successTimeAdded = new Date().getTime();
// const successMsToLive = 5;
// const action = new ResponseCacheSuccessAction(keys[0], successUUIDs, successTimeAdded, successMsToLive);
//
// it("should perform the action without affecting the previous state", () => {
// it('should perform the action without affecting the previous state', () => {
// //testState has already been frozen above
// responseCacheReducer(testState, action);
// });
//
// it("should add the response to the cached request", () => {
// it('should add the response to the cached request', () => {
// const newState = responseCacheReducer(testState, action);
// expect(newState[keys[0]].resourceUUIDs).toBe(successUUIDs);
// expect(newState[keys[0]].timeAdded).toBe(successTimeAdded);
// expect(newState[keys[0]].msToLive).toBe(successMsToLive);
// });
//
// it("should set responsePending to false", () => {
// it('should set responsePending to false', () => {
// const newState = responseCacheReducer(testState, action);
// expect(newState[keys[0]].responsePending).toBe(false);
// });
//
// it("should remove any previous error message for the request", () => {
// it('should remove any previous error message for the request', () => {
// const newState = responseCacheReducer(errorState, action);
// expect(newState[keys[0]].errorMessage).toBeUndefined();
// });
// });
//
// describe("ERROR", () => {
// describe('ERROR', () => {
// const errorMsg = 'errorMsg';
// const action = new ResponseCacheErrorAction(keys[0], errorMsg);
//
// it("should perform the action without affecting the previous state", () => {
// it('should perform the action without affecting the previous state', () => {
// //testState has already been frozen above
// responseCacheReducer(testState, action);
// });
//
// it("should set an error message for the request", () => {
// it('should set an error message for the request', () => {
// const newState = responseCacheReducer(errorState, action);
// expect(newState[keys[0]].errorMessage).toBe(errorMsg);
// });
//
// it("should set responsePending to false", () => {
// it('should set responsePending to false', () => {
// const newState = responseCacheReducer(testState, action);
// expect(newState[keys[0]].responsePending).toBe(false);
// });
// });
//
// describe("REMOVE", () => {
// it("should perform the action without affecting the previous state", () => {
// describe('REMOVE', () => {
// it('should perform the action without affecting the previous state', () => {
// const action = new ResponseCacheRemoveAction(keys[0]);
// //testState has already been frozen above
// responseCacheReducer(testState, action);
// });
//
// it("should remove the specified request from the cache", () => {
// it('should remove the specified request from the cache', () => {
// const action = new ResponseCacheRemoveAction(keys[0]);
// const newState = responseCacheReducer(testState, action);
// expect(testState[keys[0]]).not.toBeUndefined();
// expect(newState[keys[0]]).toBeUndefined();
// });
//
// it("shouldn't do anything when the specified key isn't cached", () => {
// const wrongKey = "this isn't cached";
// it('shouldn't do anything when the specified key isn't cached', () => {
// const wrongKey = 'this isn't cached';
// const action = new ResponseCacheRemoveAction(wrongKey);
// const newState = responseCacheReducer(testState, action);
// expect(testState[wrongKey]).toBeUndefined();
@@ -205,16 +205,16 @@ class NullAction extends ResponseCacheRemoveAction {
// });
// });
//
// describe("RESET_TIMESTAMPS", () => {
// describe('RESET_TIMESTAMPS', () => {
// const newTimeStamp = new Date().getTime();
// const action = new ResetResponseCacheTimestampsAction(newTimeStamp);
//
// it("should perform the action without affecting the previous state", () => {
// it('should perform the action without affecting the previous state', () => {
// //testState has already been frozen above
// responseCacheReducer(testState, action);
// });
//
// it("should set the timestamp of all requests in the cache", () => {
// it('should set the timestamp of all requests in the cache', () => {
// const newState = responseCacheReducer(testState, action);
// Object.keys(newState).forEach((key) => {
// expect(newState[key].timeAdded).toEqual(newTimeStamp);

View File

@@ -2,10 +2,10 @@ import {
ResponseCacheAction, ResponseCacheActionTypes,
ResponseCacheRemoveAction, ResetResponseCacheTimestampsAction,
ResponseCacheAddAction
} from "./response-cache.actions";
import { CacheEntry } from "./cache-entry";
import { hasValue } from "../../shared/empty.util";
import { Response } from "./response-cache.models";
} from './response-cache.actions';
import { CacheEntry } from './cache-entry';
import { hasValue } from '../../shared/empty.util';
import { Response } from './response-cache.models';
/**
* An entry in the ResponseCache
@@ -41,15 +41,15 @@ export const responseCacheReducer = (state = initialState, action: ResponseCache
switch (action.type) {
case ResponseCacheActionTypes.ADD: {
return addToCache(state, <ResponseCacheAddAction>action);
return addToCache(state, action as ResponseCacheAddAction);
}
case ResponseCacheActionTypes.REMOVE: {
return removeFromCache(state, <ResponseCacheRemoveAction>action);
return removeFromCache(state, action as ResponseCacheRemoveAction);
}
case ResponseCacheActionTypes.RESET_TIMESTAMPS: {
return resetResponseCacheTimestamps(state, <ResetResponseCacheTimestampsAction>action)
return resetResponseCacheTimestamps(state, action as ResetResponseCacheTimestampsAction)
}
default: {
@@ -81,12 +81,11 @@ function addToCache(state: ResponseCacheState, action: ResponseCacheAddAction):
*/
function removeFromCache(state: ResponseCacheState, action: ResponseCacheRemoveAction): ResponseCacheState {
if (hasValue(state[action.payload])) {
let newCache = Object.assign({}, state);
const newCache = Object.assign({}, state);
delete newCache[action.payload];
return newCache;
}
else {
} else {
return state;
}
}
@@ -102,8 +101,8 @@ function removeFromCache(state: ResponseCacheState, action: ResponseCacheRemoveA
* the new state, with all timeAdded timestamps set to the specified value
*/
function resetResponseCacheTimestamps(state: ResponseCacheState, action: ResetResponseCacheTimestampsAction): ResponseCacheState {
let newState = Object.create(null);
Object.keys(state).forEach(key => {
const newState = Object.create(null);
Object.keys(state).forEach((key) => {
newState[key] = Object.assign({}, state[key], {
timeAdded: action.payload
});

View File

@@ -1,18 +1,19 @@
import { ResponseCacheService } from "./response-cache.service";
import { Store } from "@ngrx/store";
import { ResponseCacheState, ResponseCacheEntry } from "./response-cache.reducer";
import { OpaqueToken } from "@angular/core";
import { Observable } from "rxjs";
import { OpaqueToken } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Store } from '@ngrx/store';
// describe("ResponseCacheService", () => {
import { ResponseCacheService } from './response-cache.service';
import { ResponseCacheState, ResponseCacheEntry } from './response-cache.reducer';
// describe('ResponseCacheService', () => {
// let service: ResponseCacheService;
// let store: Store<ResponseCacheState>;
//
// const keys = ["125c17f89046283c5f0640722aac9feb", "a06c3006a41caec5d635af099b0c780c"];
// const keys = ['125c17f89046283c5f0640722aac9feb', 'a06c3006a41caec5d635af099b0c780c'];
// const serviceTokens = [new OpaqueToken('service1'), new OpaqueToken('service2')];
// const resourceID = "9978";
// const paginationOptions = { "resultsPerPage": 10, "currentPage": 1 };
// const sortOptions = { "field": "id", "direction": 0 };
// const resourceID = '9978';
// const paginationOptions = { 'resultsPerPage': 10, 'currentPage': 1 };
// const sortOptions = { 'field': 'id', 'direction': 0 };
// const timestamp = new Date().getTime();
// const validCacheEntry = (key) => {
// return {
@@ -36,33 +37,33 @@ import { Observable } from "rxjs";
// spyOn(window, 'Date').and.returnValue({ getTime: () => timestamp });
// });
//
// describe("findAll", () => {
// describe('findAll', () => {
// beforeEach(() => {
// spyOn(service, "get").and.callFake((key) => Observable.of({key: key}));
// spyOn(service, 'get').and.callFake((key) => Observable.of({key: key}));
// });
// describe("if the key isn't cached", () => {
// describe('if the key isn't cached', () => {
// beforeEach(() => {
// spyOn(service, "has").and.returnValue(false);
// spyOn(service, 'has').and.returnValue(false);
// });
// it("should dispatch a FIND_ALL action with the key, service, scopeID, paginationOptions and sortOptions", () => {
// it('should dispatch a FIND_ALL action with the key, service, scopeID, paginationOptions and sortOptions', () => {
// service.findAll(keys[0], serviceTokens[0], resourceID, paginationOptions, sortOptions);
// expect(store.dispatch).toHaveBeenCalledWith(new ResponseCacheFindAllAction(keys[0], serviceTokens[0], resourceID, paginationOptions, sortOptions))
// });
// it("should return an observable of the newly cached request with the specified key", () => {
// it('should return an observable of the newly cached request with the specified key', () => {
// let result: ResponseCacheEntry;
// service.findAll(keys[0], serviceTokens[0], resourceID, paginationOptions, sortOptions).take(1).subscribe(entry => result = entry);
// expect(result.key).toEqual(keys[0]);
// });
// });
// describe("if the key is already cached", () => {
// describe('if the key is already cached', () => {
// beforeEach(() => {
// spyOn(service, "has").and.returnValue(true);
// spyOn(service, 'has').and.returnValue(true);
// });
// it("shouldn't dispatch anything", () => {
// it('shouldn't dispatch anything', () => {
// service.findAll(keys[0], serviceTokens[0], resourceID, paginationOptions, sortOptions);
// expect(store.dispatch).not.toHaveBeenCalled();
// });
// it("should return an observable of the existing cached request with the specified key", () => {
// it('should return an observable of the existing cached request with the specified key', () => {
// let result: ResponseCacheEntry;
// service.findAll(keys[0], serviceTokens[0], resourceID, paginationOptions, sortOptions).take(1).subscribe(entry => result = entry);
// expect(result.key).toEqual(keys[0]);
@@ -70,33 +71,33 @@ import { Observable } from "rxjs";
// });
// });
//
// describe("findById", () => {
// describe('findById', () => {
// beforeEach(() => {
// spyOn(service, "get").and.callFake((key) => Observable.of({key: key}));
// spyOn(service, 'get').and.callFake((key) => Observable.of({key: key}));
// });
// describe("if the key isn't cached", () => {
// describe('if the key isn't cached', () => {
// beforeEach(() => {
// spyOn(service, "has").and.returnValue(false);
// spyOn(service, 'has').and.returnValue(false);
// });
// it("should dispatch a FIND_BY_ID action with the key, service, and resourceID", () => {
// it('should dispatch a FIND_BY_ID action with the key, service, and resourceID', () => {
// service.findById(keys[0], serviceTokens[0], resourceID);
// expect(store.dispatch).toHaveBeenCalledWith(new ResponseCacheFindByIDAction(keys[0], serviceTokens[0], resourceID))
// });
// it("should return an observable of the newly cached request with the specified key", () => {
// it('should return an observable of the newly cached request with the specified key', () => {
// let result: ResponseCacheEntry;
// service.findById(keys[0], serviceTokens[0], resourceID).take(1).subscribe(entry => result = entry);
// expect(result.key).toEqual(keys[0]);
// });
// });
// describe("if the key is already cached", () => {
// describe('if the key is already cached', () => {
// beforeEach(() => {
// spyOn(service, "has").and.returnValue(true);
// spyOn(service, 'has').and.returnValue(true);
// });
// it("shouldn't dispatch anything", () => {
// it('shouldn't dispatch anything', () => {
// service.findById(keys[0], serviceTokens[0], resourceID);
// expect(store.dispatch).not.toHaveBeenCalled();
// });
// it("should return an observable of the existing cached request with the specified key", () => {
// it('should return an observable of the existing cached request with the specified key', () => {
// let result: ResponseCacheEntry;
// service.findById(keys[0], serviceTokens[0], resourceID).take(1).subscribe(entry => result = entry);
// expect(result.key).toEqual(keys[0]);
@@ -104,9 +105,9 @@ import { Observable } from "rxjs";
// });
// });
//
// describe("get", () => {
// it("should return an observable of the cached request with the specified key", () => {
// spyOn(store, "select").and.callFake((...args:Array<any>) => {
// describe('get', () => {
// it('should return an observable of the cached request with the specified key', () => {
// spyOn(store, 'select').and.callFake((...args:Array<any>) => {
// return Observable.of(validCacheEntry(args[args.length - 1]));
// });
//
@@ -115,8 +116,8 @@ import { Observable } from "rxjs";
// expect(testObj.key).toEqual(keys[1]);
// });
//
// it("should not return a cached request that has exceeded its time to live", () => {
// spyOn(store, "select").and.callFake((...args:Array<any>) => {
// it('should not return a cached request that has exceeded its time to live', () => {
// spyOn(store, 'select').and.callFake((...args:Array<any>) => {
// return Observable.of(invalidCacheEntry(args[args.length - 1]));
// });
//
@@ -127,18 +128,18 @@ import { Observable } from "rxjs";
// });
// });
//
// describe("has", () => {
// it("should return true if the request with the supplied key is cached and still valid", () => {
// describe('has', () => {
// it('should return true if the request with the supplied key is cached and still valid', () => {
// spyOn(store, 'select').and.returnValue(Observable.of(validCacheEntry(keys[1])));
// expect(service.has(keys[1])).toBe(true);
// });
//
// it("should return false if the request with the supplied key isn't cached", () => {
// it('should return false if the request with the supplied key isn't cached', () => {
// spyOn(store, 'select').and.returnValue(Observable.of(undefined));
// expect(service.has(keys[1])).toBe(false);
// });
//
// it("should return false if the request with the supplied key is cached but has exceeded its time to live", () => {
// it('should return false if the request with the supplied key is cached but has exceeded its time to live', () => {
// spyOn(store, 'select').and.returnValue(Observable.of(invalidCacheEntry(keys[1])));
// expect(service.has(keys[1])).toBe(false);
// });

View File

@@ -1,15 +1,12 @@
import { Injectable } from "@angular/core";
import { Store } from "@ngrx/store";
import {
ResponseCacheState, ResponseCacheEntry
} from "./response-cache.reducer";
import { Observable } from "rxjs";
import { hasNoValue } from "../../shared/empty.util";
import {
ResponseCacheRemoveAction,
ResponseCacheAddAction
} from "./response-cache.actions";
import { Response } from "./response-cache.models";
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { ResponseCacheState, ResponseCacheEntry } from './response-cache.reducer';
import { hasNoValue } from '../../shared/empty.util';
import { ResponseCacheRemoveAction, ResponseCacheAddAction } from './response-cache.actions';
import { Response } from './response-cache.models';
/**
* A service to interact with the response cache
@@ -38,7 +35,7 @@ export class ResponseCacheService {
*/
get(key: string): Observable<ResponseCacheEntry> {
return this.store.select<ResponseCacheEntry>('core', 'cache', 'response', key)
.filter(entry => this.isValid(entry))
.filter((entry) => this.isValid(entry))
.distinctUntilChanged()
}
@@ -56,7 +53,7 @@ export class ResponseCacheService {
this.store.select<ResponseCacheEntry>('core', 'cache', 'response', key)
.take(1)
.subscribe(entry => {
.subscribe((entry) => {
result = this.isValid(entry);
});
@@ -75,8 +72,7 @@ export class ResponseCacheService {
private isValid(entry: ResponseCacheEntry): boolean {
if (hasNoValue(entry)) {
return false;
}
else {
} else {
const timeOutdated = entry.timeAdded + entry.msToLive;
const isOutDated = new Date().getTime() > timeOutdated;
if (isOutDated) {

View File

@@ -1,8 +1,9 @@
import { EffectsModule } from "@ngrx/effects";
import { ObjectCacheEffects } from "./data/object-cache.effects";
import { RequestCacheEffects } from "./data/request-cache.effects";
import { HrefIndexEffects } from "./index/href-index.effects";
import { RequestEffects } from "./data/request.effects";
import { EffectsModule } from '@ngrx/effects';
import { ObjectCacheEffects } from './data/object-cache.effects';
import { RequestCacheEffects } from './data/request-cache.effects';
import { HrefIndexEffects } from './index/href-index.effects';
import { RequestEffects } from './data/request.effects';
export const coreEffects = [
EffectsModule.run(RequestEffects),

View File

@@ -1,18 +1,19 @@
import { NgModule, Optional, SkipSelf, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from "../shared/shared.module";
import { isNotEmpty } from "../shared/empty.util";
import { FooterComponent } from "./footer/footer.component";
import { DSpaceRESTv2Service } from "./dspace-rest-v2/dspace-rest-v2.service";
import { ObjectCacheService } from "./cache/object-cache.service";
import { ResponseCacheService } from "./cache/response-cache.service";
import { CollectionDataService } from "./data/collection-data.service";
import { ItemDataService } from "./data/item-data.service";
import { RequestService } from "./data/request.service";
import { RemoteDataBuildService } from "./cache/builders/remote-data-build.service";
import { CommunityDataService } from "./data/community-data.service";
import { PaginationComponentOptions } from "../shared/pagination/pagination-component-options.model";
import { SharedModule } from '../shared/shared.module';
import { isNotEmpty } from '../shared/empty.util';
import { FooterComponent } from './footer/footer.component';
import { DSpaceRESTv2Service } from './dspace-rest-v2/dspace-rest-v2.service';
import { ObjectCacheService } from './cache/object-cache.service';
import { ResponseCacheService } from './cache/response-cache.service';
import { CollectionDataService } from './data/collection-data.service';
import { ItemDataService } from './data/item-data.service';
import { RequestService } from './data/request.service';
import { RemoteDataBuildService } from './cache/builders/remote-data-build.service';
import { CommunityDataService } from './data/community-data.service';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
const IMPORTS = [
CommonModule,
@@ -46,12 +47,6 @@ const PROVIDERS = [
providers: [...PROVIDERS]
})
export class CoreModule {
constructor( @Optional() @SkipSelf() parentModule: CoreModule) {
if (isNotEmpty(parentModule)) {
throw new Error(
'CoreModule is already loaded. Import it in the AppModule only');
}
}
static forRoot(): ModuleWithProviders {
return {
@@ -61,4 +56,11 @@ export class CoreModule {
]
};
}
constructor( @Optional() @SkipSelf() parentModule: CoreModule) {
if (isNotEmpty(parentModule)) {
throw new Error('CoreModule is already loaded. Import it in the AppModule only');
}
}
}

View File

@@ -1,7 +1,8 @@
import { combineReducers } from "@ngrx/store";
import { CacheState, cacheReducer } from "./cache/cache.reducers";
import { IndexState, indexReducer } from "./index/index.reducers";
import { DataState, dataReducer } from "./data/data.reducers";
import { combineReducers } from '@ngrx/store';
import { CacheState, cacheReducer } from './cache/cache.reducers';
import { IndexState, indexReducer } from './index/index.reducers';
import { DataState, dataReducer } from './data/data.reducers';
export interface CoreState {
cache: CacheState,

View File

@@ -1,14 +1,15 @@
import { Inject, Injectable } from "@angular/core";
import { DataService } from "./data.service";
import { Collection } from "../shared/collection.model";
import { ObjectCacheService } from "../cache/object-cache.service";
import { ResponseCacheService } from "../cache/response-cache.service";
import { Store } from "@ngrx/store";
import { NormalizedCollection } from "../cache/models/normalized-collection.model";
import { CoreState } from "../core.reducers";
import { RequestService } from "./request.service";
import { RemoteDataBuildService } from "../cache/builders/remote-data-build.service";
import { GLOBAL_CONFIG, GlobalConfig } from "../../../config";
import { Inject, Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { DataService } from './data.service';
import { Collection } from '../shared/collection.model';
import { ObjectCacheService } from '../cache/object-cache.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { NormalizedCollection } from '../cache/models/normalized-collection.model';
import { CoreState } from '../core.reducers';
import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
@Injectable()
export class CollectionDataService extends DataService<NormalizedCollection, Collection> {

View File

@@ -1,14 +1,16 @@
import { Inject, Injectable } from "@angular/core";
import { DataService } from "./data.service";
import { Community } from "../shared/community.model";
import { ObjectCacheService } from "../cache/object-cache.service";
import { ResponseCacheService } from "../cache/response-cache.service";
import { Store } from "@ngrx/store";
import { NormalizedCommunity } from "../cache/models/normalized-community.model";
import { CoreState } from "../core.reducers";
import { RequestService } from "./request.service";
import { RemoteDataBuildService } from "../cache/builders/remote-data-build.service";
import { GLOBAL_CONFIG, GlobalConfig } from "../../../config";
import { Inject, Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { DataService } from './data.service';
import { Community } from '../shared/community.model';
import { ObjectCacheService } from '../cache/object-cache.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { NormalizedCommunity } from '../cache/models/normalized-community.model';
import { CoreState } from '../core.reducers';
import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
@Injectable()
export class CommunityDataService extends DataService<NormalizedCommunity, Community> {

View File

@@ -1,5 +1,6 @@
import { combineReducers } from "@ngrx/store";
import { RequestState, requestReducer } from "./request.reducer";
import { combineReducers } from '@ngrx/store';
import { RequestState, requestReducer } from './request.reducer';
export interface DataState {
request: RequestState

View File

@@ -1,18 +1,18 @@
import { ObjectCacheService } from "../cache/object-cache.service";
import { ResponseCacheService } from "../cache/response-cache.service";
import { CacheableObject } from "../cache/object-cache.reducer";
import { hasValue, isNotEmpty } from "../../shared/empty.util";
import { RemoteData } from "./remote-data";
import { FindAllOptions, FindAllRequest, FindByIDRequest, Request } from "./request.models";
import { Store } from "@ngrx/store";
import { RequestConfigureAction, RequestExecuteAction } from "./request.actions";
import { CoreState } from "../core.reducers";
import { RequestService } from "./request.service";
import { RemoteDataBuildService } from "../cache/builders/remote-data-build.service";
import { GenericConstructor } from "../shared/generic-constructor";
import { Inject } from "@angular/core";
import { GLOBAL_CONFIG, GlobalConfig } from "../../../config";
import { RESTURLCombiner } from "../url-combiner/rest-url-combiner";
import { ObjectCacheService } from '../cache/object-cache.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { CacheableObject } from '../cache/object-cache.reducer';
import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { RemoteData } from './remote-data';
import { FindAllOptions, FindAllRequest, FindByIDRequest, Request } from './request.models';
import { Store } from '@ngrx/store';
import { RequestConfigureAction, RequestExecuteAction } from './request.actions';
import { CoreState } from '../core.reducers';
import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { GenericConstructor } from '../shared/generic-constructor';
import { Inject } from '@angular/core';
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
export abstract class DataService<TNormalized extends CacheableObject, TDomain> {
protected abstract objectCache: ObjectCacheService;
@@ -32,17 +32,16 @@ export abstract class DataService<TNormalized extends CacheableObject, TDomain>
protected getFindAllHref(options: FindAllOptions = {}): string {
let result;
let args = [];
const args = [];
if (hasValue(options.scopeID)) {
result = this.browseEndpoint;
args.push(`scope=${options.scopeID}`);
}
else {
} else {
result = this.resourceEndpoint;
}
if (hasValue(options.currentPage) && typeof options.currentPage === "number") {
if (hasValue(options.currentPage) && typeof options.currentPage === 'number') {
/* TODO: this is a temporary fix for the pagination start index (0 or 1) discrepancy between the rest and the frontend respectively */
args.push(`page=${options.currentPage - 1}`);
}
@@ -65,7 +64,7 @@ export abstract class DataService<TNormalized extends CacheableObject, TDomain>
return new RESTURLCombiner(this.EnvConfig, result).toString();
}
findAll(options: FindAllOptions = {}): RemoteData<Array<TDomain>> {
findAll(options: FindAllOptions = {}): RemoteData<TDomain[]> {
const href = this.getFindAllHref(options);
const request = new FindAllRequest(href, options);
this.requestService.configure(request);

View File

@@ -1,14 +1,16 @@
import { Inject, Injectable } from "@angular/core";
import { DataService } from "./data.service";
import { Item } from "../shared/item.model";
import { ObjectCacheService } from "../cache/object-cache.service";
import { ResponseCacheService } from "../cache/response-cache.service";
import { Store } from "@ngrx/store";
import { CoreState } from "../core.reducers";
import { NormalizedItem } from "../cache/models/normalized-item.model";
import { RequestService } from "./request.service";
import { RemoteDataBuildService } from "../cache/builders/remote-data-build.service";
import { GLOBAL_CONFIG, GlobalConfig } from "../../../config";
import { Inject, Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { DataService } from './data.service';
import { Item } from '../shared/item.model';
import { ObjectCacheService } from '../cache/object-cache.service';
import { ResponseCacheService } from '../cache/response-cache.service';
import { CoreState } from '../core.reducers';
import { NormalizedItem } from '../cache/models/normalized-item.model';
import { RequestService } from './request.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
@Injectable()
export class ItemDataService extends DataService<NormalizedItem, Item> {

View File

@@ -1,18 +1,15 @@
import { Injectable } from "@angular/core";
import { Actions, Effect } from "@ngrx/effects";
import { StoreActionTypes } from "../../store.actions";
import { ResetObjectCacheTimestampsAction } from "../cache/object-cache.actions";
import { Injectable } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';
import { StoreActionTypes } from '../../store.actions';
import { ResetObjectCacheTimestampsAction } from '../cache/object-cache.actions';
@Injectable()
export class ObjectCacheEffects {
constructor(
private actions$: Actions
) { }
/**
* When the store is rehydrated in the browser, set all cache
* timestamps to "now", because the time zone of the server can
* timestamps to 'now', because the time zone of the server can
* differ from the client.
*
* This assumes that the server cached everything a negligible
@@ -22,4 +19,6 @@ export class ObjectCacheEffects {
.ofType(StoreActionTypes.REHYDRATE)
.map(() => new ResetObjectCacheTimestampsAction(new Date().getTime()));
constructor(private actions$: Actions) { }
}

View File

@@ -1,11 +1,12 @@
import { Observable } from "rxjs";
import { PageInfo } from "../shared/page-info.model";
import { Observable } from 'rxjs/Observable';
import { PageInfo } from '../shared/page-info.model';
export enum RemoteDataState {
RequestPending = <any>"RequestPending",
ResponsePending = <any>"ResponsePending",
Failed = <any>"Failed",
Success = <any>"Success"
RequestPending = 'RequestPending' as any,
ResponsePending = 'ResponsePending' as any,
Failed = 'Failed' as any,
Success = 'Success' as any
}
/**
@@ -32,14 +33,11 @@ export class RemoteData<T> {
(requestPending, responsePending, isSuccessFul) => {
if (requestPending) {
return RemoteDataState.RequestPending
}
else if (responsePending) {
} else if (responsePending) {
return RemoteDataState.ResponsePending
}
else if (!isSuccessFul) {
} else if (!isSuccessFul) {
return RemoteDataState.Failed
}
else {
} else {
return RemoteDataState.Success
}
}
@@ -47,36 +45,26 @@ export class RemoteData<T> {
}
get isRequestPending(): Observable<boolean> {
return this.state
.map(state => state == RemoteDataState.RequestPending)
.distinctUntilChanged();
return this.state.map((state) => state === RemoteDataState.RequestPending).distinctUntilChanged();
}
get isResponsePending(): Observable<boolean> {
return this.state
.map(state => state == RemoteDataState.ResponsePending)
.distinctUntilChanged();
return this.state.map((state) => state === RemoteDataState.ResponsePending).distinctUntilChanged();
}
get isLoading(): Observable<boolean> {
return this.state
.map(state => {
return state == RemoteDataState.RequestPending
|| state === RemoteDataState.ResponsePending
})
.distinctUntilChanged();
return this.state.map((state) => {
return state === RemoteDataState.RequestPending
|| state === RemoteDataState.ResponsePending
}).distinctUntilChanged();
}
get hasFailed(): Observable<boolean> {
return this.state
.map(state => state == RemoteDataState.Failed)
.distinctUntilChanged();
return this.state.map((state) => state === RemoteDataState.Failed).distinctUntilChanged();
}
get hasSucceeded(): Observable<boolean> {
return this.state
.map(state => state == RemoteDataState.Success)
.distinctUntilChanged();
return this.state.map((state) => state === RemoteDataState.Success).distinctUntilChanged();
}
}

View File

@@ -1,18 +1,15 @@
import { Injectable, Inject } from "@angular/core";
import { Actions, Effect } from "@ngrx/effects";
import { ObjectCacheActionTypes } from "../cache/object-cache.actions";
import { ResetResponseCacheTimestampsAction } from "../cache/response-cache.actions";
import { Injectable, Inject } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';
import { ObjectCacheActionTypes } from '../cache/object-cache.actions';
import { ResetResponseCacheTimestampsAction } from '../cache/response-cache.actions';
@Injectable()
export class RequestCacheEffects {
constructor(
private actions$: Actions,
) { }
/**
* When the store is rehydrated in the browser, set all cache
* timestamps to "now", because the time zone of the server can
* timestamps to 'now', because the time zone of the server can
* differ from the client.
*
* This assumes that the server cached everything a negligible
@@ -29,4 +26,7 @@ export class RequestCacheEffects {
@Effect() fixTimestampsOnRehydrate = this.actions$
.ofType(ObjectCacheActionTypes.RESET_TIMESTAMPS)
.map(() => new ResetResponseCacheTimestampsAction(new Date().getTime()));
constructor(private actions$: Actions, ) { }
}

View File

@@ -1,7 +1,7 @@
import { Action } from "@ngrx/store";
import { type } from "../../shared/ngrx/type";
import { CacheableObject } from "../cache/object-cache.reducer";
import { Request } from "./request.models";
import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type';
import { CacheableObject } from '../cache/object-cache.reducer';
import { Request } from './request.models';
/**
* The list of RequestAction type definitions
@@ -12,6 +12,7 @@ export const RequestActionTypes = {
COMPLETE: type('dspace/core/data/request/COMPLETE')
};
/* tslint:disable:max-classes-per-file */
export class RequestConfigureAction implements Action {
type = RequestActionTypes.CONFIGURE;
payload: Request<CacheableObject>;
@@ -49,6 +50,7 @@ export class RequestCompleteAction implements Action {
this.payload = key;
}
}
/* tslint:enable:max-classes-per-file */
/**
* A type to encompass all RequestActions

View File

@@ -1,26 +1,27 @@
import { Injectable, Inject } from "@angular/core";
import { Actions, Effect } from "@ngrx/effects";
import { DSpaceRESTv2Service } from "../dspace-rest-v2/dspace-rest-v2.service";
import { ObjectCacheService } from "../cache/object-cache.service";
import { DSpaceRESTV2Response } from "../dspace-rest-v2/dspace-rest-v2-response.model";
import { DSpaceRESTv2Serializer } from "../dspace-rest-v2/dspace-rest-v2.serializer";
import { CacheableObject } from "../cache/object-cache.reducer";
import { Observable } from "rxjs";
import { Response, SuccessResponse, ErrorResponse } from "../cache/response-cache.models";
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from "../../shared/empty.util";
import { GlobalConfig, GLOBAL_CONFIG } from "../../../config";
import { RequestEntry } from "./request.reducer";
import {
RequestActionTypes, RequestExecuteAction,
RequestCompleteAction
} from "./request.actions";
import { ResponseCacheService } from "../cache/response-cache.service";
import { RequestService } from "./request.service";
import { NormalizedObjectFactory } from "../cache/models/normalized-object-factory";
import { ResourceType } from "../shared/resource-type";
import { RequestError } from "./request.models";
import { PageInfo } from "../shared/page-info.model";
import { NormalizedObject } from "../cache/models/normalized-object.model";
import { Injectable, Inject } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';
// tslint:disable-next-line:import-blacklist
import { Observable } from 'rxjs';
import { DSpaceRESTv2Service } from '../dspace-rest-v2/dspace-rest-v2.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
import { CacheableObject } from '../cache/object-cache.reducer';
import { Response, SuccessResponse, ErrorResponse } from '../cache/response-cache.models';
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
import { RequestEntry } from './request.reducer';
import { RequestActionTypes, RequestExecuteAction, RequestCompleteAction } from './request.actions';
import { ResponseCacheService } from '../cache/response-cache.service';
import { RequestService } from './request.service';
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
import { ResourceType } from '../shared/resource-type';
import { RequestError } from './request.models';
import { PageInfo } from '../shared/page-info.model';
import { NormalizedObject } from '../cache/models/normalized-object.model';
import { GlobalConfig, GLOBAL_CONFIG } from '../../../config';
function isObjectLevel(halObj: any) {
return isNotEmpty(halObj._links) && hasValue(halObj._links.self);
@@ -38,23 +39,14 @@ function flattenSingleKeyObject(obj: any): any {
return obj[keys[0]];
}
/* tslint:disable:max-classes-per-file */
class ProcessRequestDTO {
[key: string]: NormalizedObject[]
}
@Injectable()
export class RequestEffects {
constructor(
@Inject(GLOBAL_CONFIG) private config: GlobalConfig,
private actions$: Actions,
private restApi: DSpaceRESTv2Service,
private objectCache: ObjectCacheService,
private responseCache: ResponseCacheService,
protected requestService: RequestService
) { }
@Effect() execute = this.actions$
.ofType(RequestActionTypes.EXECUTE)
.flatMap((action: RequestExecuteAction) => {
@@ -65,48 +57,52 @@ export class RequestEffects {
return this.restApi.get(entry.request.href)
.map((data: DSpaceRESTV2Response) => {
const processRequestDTO = this.process(data.payload, entry.request.href);
const uuids = flattenSingleKeyObject(processRequestDTO).map(no => no.uuid);
const uuids = flattenSingleKeyObject(processRequestDTO).map((no) => no.uuid);
return new SuccessResponse(uuids, data.statusCode, this.processPageInfo(data.payload.page))
}).do((response: Response) => this.responseCache.add(entry.request.href, response, this.config.cache.msToLive))
}).do((response: Response) => this.responseCache.add(entry.request.href, response, this.EnvConfig.cache.msToLive))
.map((response: Response) => new RequestCompleteAction(entry.request.href))
.catch((error: RequestError) => Observable.of(new ErrorResponse(error))
.do((response: Response) => this.responseCache.add(entry.request.href, response, this.config.cache.msToLive))
.do((response: Response) => this.responseCache.add(entry.request.href, response, this.EnvConfig.cache.msToLive))
.map((response: Response) => new RequestCompleteAction(entry.request.href)));
});
constructor(
@Inject(GLOBAL_CONFIG) private EnvConfig: GlobalConfig,
private actions$: Actions,
private restApi: DSpaceRESTv2Service,
private objectCache: ObjectCacheService,
private responseCache: ResponseCacheService,
protected requestService: RequestService
) { }
protected process(data: any, requestHref: string): ProcessRequestDTO {
if (isNotEmpty(data)) {
if (isPaginatedResponse(data)) {
return this.process(data._embedded, requestHref);
}
else if (isObjectLevel(data)) {
return { "topLevel": this.deserializeAndCache(data, requestHref) };
}
else {
let result = new ProcessRequestDTO();
} else if (isObjectLevel(data)) {
return { topLevel: this.deserializeAndCache(data, requestHref) };
} else {
const result = new ProcessRequestDTO();
if (Array.isArray(data)) {
result['topLevel'] = [];
data.forEach(datum => {
result.topLevel = [];
data.forEach((datum) => {
if (isPaginatedResponse(datum)) {
const obj = this.process(datum, requestHref);
result['topLevel'] = [...result['topLevel'], ...flattenSingleKeyObject(obj)];
}
else {
result['topLevel'] = [...result['topLevel'], ...this.deserializeAndCache(datum, requestHref)];
result.topLevel = [...result.topLevel, ...flattenSingleKeyObject(obj)];
} else {
result.topLevel = [...result.topLevel, ...this.deserializeAndCache(datum, requestHref)];
}
});
}
else {
} else {
Object.keys(data)
.filter(property => data.hasOwnProperty(property))
.filter(property => hasValue(data[property]))
.forEach(property => {
.filter((property) => data.hasOwnProperty(property))
.filter((property) => hasValue(data[property]))
.forEach((property) => {
if (isPaginatedResponse(data[property])) {
const obj = this.process(data[property], requestHref);
result[property] = flattenSingleKeyObject(obj);
}
else {
} else {
result[property] = this.deserializeAndCache(data[property], requestHref);
}
});
@@ -119,11 +115,11 @@ export class RequestEffects {
protected deserializeAndCache(obj, requestHref: string): NormalizedObject[] {
if (Array.isArray(obj)) {
let result = [];
obj.forEach(o => result = [...result, ...this.deserializeAndCache(o, requestHref)])
obj.forEach((o) => result = [...result, ...this.deserializeAndCache(o, requestHref)])
return result;
}
let type: ResourceType = obj["type"];
const type: ResourceType = obj.type;
if (hasValue(type)) {
const normObjConstructor = NormalizedObjectFactory.getConstructor(type);
@@ -134,11 +130,11 @@ export class RequestEffects {
if (isNotEmpty(obj._embedded)) {
processed = this.process(obj._embedded, requestHref);
}
let normalizedObj = serializer.deserialize(obj);
const normalizedObj = serializer.deserialize(obj);
if (isNotEmpty(processed)) {
let linksOnly = {};
Object.keys(processed).forEach(key => {
const linksOnly = {};
Object.keys(processed).forEach((key) => {
linksOnly[key] = processed[key].map((no: NormalizedObject) => no.self);
});
Object.assign(normalizedObj, linksOnly);
@@ -147,16 +143,14 @@ export class RequestEffects {
this.addToObjectCache(normalizedObj, requestHref);
return [normalizedObj];
}
else {
//TODO move check to Validator?
} else {
// TODO: move check to Validator?
// throw new Error(`The server returned an object with an unknown a known type: ${type}`);
return [];
}
}
else {
//TODO move check to Validator
} else {
// TODO: move check to Validator
// throw new Error(`The server returned an object without a type: ${JSON.stringify(obj)}`);
return [];
}
@@ -166,16 +160,16 @@ export class RequestEffects {
if (hasNoValue(co) || hasNoValue(co.uuid)) {
throw new Error('The server returned an invalid object');
}
this.objectCache.add(co, this.config.cache.msToLive, requestHref);
this.objectCache.add(co, this.EnvConfig.cache.msToLive, requestHref);
}
protected processPageInfo(pageObj: any): PageInfo {
if (isNotEmpty(pageObj)) {
return new DSpaceRESTv2Serializer(PageInfo).deserialize(pageObj);
}
else {
} else {
return undefined;
}
}
}
/* tslint:enable:max-classes-per-file */

View File

@@ -1,7 +1,8 @@
import { SortOptions } from "../cache/models/sort-options.model";
import { PaginationComponentOptions } from "../../shared/pagination/pagination-component-options.model";
import { GenericConstructor } from "../shared/generic-constructor";
import { SortOptions } from '../cache/models/sort-options.model';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { GenericConstructor } from '../shared/generic-constructor';
/* tslint:disable:max-classes-per-file */
export class Request<T> {
constructor(
public href: string,
@@ -36,3 +37,4 @@ export class FindAllRequest<T> extends Request<T> {
export class RequestError extends Error {
statusText: string;
}
/* tslint:enable:max-classes-per-file */

View File

@@ -1,9 +1,9 @@
import { CacheableObject } from "../cache/object-cache.reducer";
import { CacheableObject } from '../cache/object-cache.reducer';
import {
RequestActionTypes, RequestAction, RequestConfigureAction,
RequestExecuteAction, RequestCompleteAction
} from "./request.actions";
import { Request } from "./request.models";
} from './request.actions';
import { Request } from './request.models';
export class RequestEntry {
request: Request<CacheableObject>;
@@ -12,7 +12,6 @@ export class RequestEntry {
completed: boolean;
}
export interface RequestState {
[key: string]: RequestEntry
}
@@ -24,15 +23,15 @@ export const requestReducer = (state = initialState, action: RequestAction): Req
switch (action.type) {
case RequestActionTypes.CONFIGURE: {
return configureRequest(state, <RequestConfigureAction>action);
return configureRequest(state, action as RequestConfigureAction);
}
case RequestActionTypes.EXECUTE: {
return executeRequest(state, <RequestExecuteAction>action);
return executeRequest(state, action as RequestExecuteAction);
}
case RequestActionTypes.COMPLETE: {
return completeRequest(state, <RequestCompleteAction>action);
return completeRequest(state, action as RequestCompleteAction);
}
default: {

View File

@@ -1,16 +1,20 @@
import { Injectable } from "@angular/core";
import { RequestEntry, RequestState } from "./request.reducer";
import { Store } from "@ngrx/store";
import { Request } from "./request.models";
import { hasValue } from "../../shared/empty.util";
import { Observable } from "rxjs/Observable";
import { RequestConfigureAction, RequestExecuteAction } from "./request.actions";
import { ResponseCacheService } from "../cache/response-cache.service";
import { ObjectCacheService } from "../cache/object-cache.service";
import { CacheableObject } from "../cache/object-cache.reducer";
import { ResponseCacheEntry } from "../cache/response-cache.reducer";
import { request } from "http";
import { SuccessResponse } from "../cache/response-cache.models";
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { request } from 'http';
import { Observable } from 'rxjs/Observable';
import { RequestEntry, RequestState } from './request.reducer';
import { Request } from './request.models';
import { hasValue } from '../../shared/empty.util';
import { RequestConfigureAction, RequestExecuteAction } from './request.actions';
import { ResponseCacheService } from '../cache/response-cache.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { CacheableObject } from '../cache/object-cache.reducer';
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
import { SuccessResponse } from '../cache/response-cache.models';
@Injectable()
export class RequestService {
@@ -41,14 +45,14 @@ export class RequestService {
let isCached = this.objectCache.hasBySelfLink(request.href);
if (!isCached && this.responseCache.has(request.href)) {
//if it isn't cached it may be a list endpoint, if so verify
//every object included in the response is still cached
// if it isn't cached it may be a list endpoint, if so verify
// every object included in the response is still cached
this.responseCache.get(request.href)
.take(1)
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
.map((entry: ResponseCacheEntry) => (<SuccessResponse>entry.response).resourceUUIDs)
.map((resourceUUIDs: Array<string>) => resourceUUIDs.every(uuid => this.objectCache.has(uuid)))
.subscribe(c => isCached = c);
.map((entry: ResponseCacheEntry) => (entry.response as SuccessResponse).resourceUUIDs)
.map((resourceUUIDs: string[]) => resourceUUIDs.every((uuid) => this.objectCache.has(uuid)))
.subscribe((c) => isCached = c);
}
const isPending = this.isPending(request.href);

View File

@@ -1,5 +1,6 @@
import { DSpaceRESTv2Serializer } from "./dspace-rest-v2.serializer";
import { autoserialize, autoserializeAs } from "cerialize";
import { autoserialize, autoserializeAs } from 'cerialize';
import { DSpaceRESTv2Serializer } from './dspace-rest-v2.serializer';
class TestModel {
@autoserialize
@@ -9,55 +10,54 @@ class TestModel {
name: string;
@autoserializeAs(TestModel)
parents?: Array<TestModel>;
parents?: TestModel[];
}
const testModels = [
{
"id": "d4466d54-d73b-4d8f-b73f-c702020baa14",
"name": "Model 1",
id: 'd4466d54-d73b-4d8f-b73f-c702020baa14',
name: 'Model 1',
},
{
"id": "752a1250-949a-46ad-9bea-fbc45f0b656d",
"name": "Model 2",
id: '752a1250-949a-46ad-9bea-fbc45f0b656d',
name: 'Model 2',
}
];
const testResponses = [
{
"_links": {
"self": "/testmodels/9e32a2e2-6b91-4236-a361-995ccdc14c60",
"parents": [
{ "href": "/testmodels/21539b1d-9ef1-4eda-9c77-49565b5bfb78" },
{ "href": "/testmodels/be8325f7-243b-49f4-8a4b-df2b793ff3b5" }
_links: {
self: '/testmodels/9e32a2e2-6b91-4236-a361-995ccdc14c60',
parents: [
{ href: '/testmodels/21539b1d-9ef1-4eda-9c77-49565b5bfb78' },
{ href: '/testmodels/be8325f7-243b-49f4-8a4b-df2b793ff3b5' }
]
},
"id": "9e32a2e2-6b91-4236-a361-995ccdc14c60",
"type": "testModels",
"name": "A Test Model"
id: '9e32a2e2-6b91-4236-a361-995ccdc14c60',
type: 'testModels',
name: 'A Test Model'
},
{
"_links": {
"self": "/testmodels/598ce822-c357-46f3-ab70-63724d02d6ad",
"parents": [
{ "href": "/testmodels/be8325f7-243b-49f4-8a4b-df2b793ff3b5" },
{ "href": "/testmodels/21539b1d-9ef1-4eda-9c77-49565b5bfb78" }
_links: {
self: '/testmodels/598ce822-c357-46f3-ab70-63724d02d6ad',
parents: [
{ href: '/testmodels/be8325f7-243b-49f4-8a4b-df2b793ff3b5' },
{ href: '/testmodels/21539b1d-9ef1-4eda-9c77-49565b5bfb78' }
]
},
"id": "598ce822-c357-46f3-ab70-63724d02d6ad",
"type": "testModels",
"name": "Another Test Model"
id: '598ce822-c357-46f3-ab70-63724d02d6ad',
type: 'testModels',
name: 'Another Test Model'
}
];
const parentHrefRegex = /^\/testmodels\/(.+)$/g;
describe('DSpaceRESTv2Serializer', () => {
describe("DSpaceRESTv2Serializer", () => {
describe('serialize', () => {
describe("serialize", () => {
it("should turn a model in to a valid document", () => {
it('should turn a model in to a valid document', () => {
const serializer = new DSpaceRESTv2Serializer(TestModel);
const doc = serializer.serialize(testModels[0]);
expect(testModels[0].id).toBe(doc.id);
@@ -66,9 +66,9 @@ describe("DSpaceRESTv2Serializer", () => {
});
describe("serializeArray", () => {
describe('serializeArray', () => {
it("should turn an array of models in to a valid document", () => {
it('should turn an array of models in to a valid document', () => {
const serializer = new DSpaceRESTv2Serializer(TestModel);
const doc = serializer.serializeArray(testModels);
@@ -80,9 +80,9 @@ describe("DSpaceRESTv2Serializer", () => {
});
describe("deserialize", () => {
describe('deserialize', () => {
it("should turn a valid document describing a single entity in to a valid model", () => {
it('should turn a valid document describing a single entity in to a valid model', () => {
const serializer = new DSpaceRESTv2Serializer(TestModel);
const model = serializer.deserialize(testResponses[0]);
@@ -90,12 +90,12 @@ describe("DSpaceRESTv2Serializer", () => {
expect(model.name).toBe(testResponses[0].name);
});
//TODO cant implement/test this yet - depends on how relationships
// TODO: cant implement/test this yet - depends on how relationships
// will be handled in the rest api
// it("should retain relationship information", () => {
// it('should retain relationship information', () => {
// const serializer = new DSpaceRESTv2Serializer(TestModel);
// const doc = {
// "_embedded": testResponses[0],
// '_embedded': testResponses[0],
// };
//
// const model = serializer.deserialize(doc);
@@ -112,7 +112,7 @@ describe("DSpaceRESTv2Serializer", () => {
// });
// TODO enable once validation is enabled in the serializer
// it("should throw an error when dealing with an invalid document", () => {
// it('should throw an error when dealing with an invalid document', () => {
// const serializer = new DSpaceRESTv2Serializer(TestModel);
// const doc = testResponses[0];
//
@@ -121,7 +121,7 @@ describe("DSpaceRESTv2Serializer", () => {
// }).toThrow();
// });
it("should throw an error when dealing with a document describing an array", () => {
it('should throw an error when dealing with a document describing an array', () => {
const serializer = new DSpaceRESTv2Serializer(TestModel);
expect(() => {
serializer.deserialize(testResponses);
@@ -130,13 +130,13 @@ describe("DSpaceRESTv2Serializer", () => {
});
describe("deserializeArray", () => {
describe('deserializeArray', () => {
//TODO rewrite to incorporate normalisation.
// it("should turn a valid document describing a collection of objects in to an array of valid models", () => {
// TODO: rewrite to incorporate normalisation.
// it('should turn a valid document describing a collection of objects in to an array of valid models', () => {
// const serializer = new DSpaceRESTv2Serializer(TestModel);
// const doc = {
// "_embedded": testResponses
// '_embedded': testResponses
// };
//
// const models = serializer.deserializeArray(doc);
@@ -147,12 +147,12 @@ describe("DSpaceRESTv2Serializer", () => {
// expect(models[1].name).toBe(doc._embedded[1].name);
// });
//TODO cant implement/test this yet - depends on how relationships
// TODO: cant implement/test this yet - depends on how relationships
// will be handled in the rest api
// it("should retain relationship information", () => {
// it('should retain relationship information', () => {
// const serializer = new DSpaceRESTv2Serializer(TestModel);
// const doc = {
// "_embedded": testResponses,
// '_embedded': testResponses,
// };
//
// const models = serializer.deserializeArray(doc);
@@ -169,7 +169,7 @@ describe("DSpaceRESTv2Serializer", () => {
// });
// TODO enable once validation is enabled in the serializer
// it("should throw an error when dealing with an invalid document", () => {
// it('should throw an error when dealing with an invalid document', () => {
// const serializer = new DSpaceRESTv2Serializer(TestModel);
// const doc = testResponses[0];
//
@@ -178,10 +178,10 @@ describe("DSpaceRESTv2Serializer", () => {
// }).toThrow();
// });
it("should throw an error when dealing with a document describing a single model", () => {
it('should throw an error when dealing with a document describing a single model', () => {
const serializer = new DSpaceRESTv2Serializer(TestModel);
const doc = {
"_embedded": testResponses[0]
_embedded: testResponses[0]
};
expect(() => {

View File

@@ -1,9 +1,10 @@
import { Serialize, Deserialize } from "cerialize";
import { Serializer } from "../serializer";
import { DSpaceRESTV2Response } from "./dspace-rest-v2-response.model";
import { DSpaceRESTv2Validator } from "./dspace-rest-v2.validator";
import { GenericConstructor } from "../shared/generic-constructor";
import { hasNoValue, hasValue } from "../../shared/empty.util";
import { Serialize, Deserialize } from 'cerialize';
import { Serializer } from '../serializer';
import { DSpaceRESTV2Response } from './dspace-rest-v2-response.model';
import { DSpaceRESTv2Validator } from './dspace-rest-v2.validator';
import { GenericConstructor } from '../shared/generic-constructor';
import { hasNoValue, hasValue } from '../../shared/empty.util';
/**
* This Serializer turns responses from v2 of DSpace's REST API
@@ -36,7 +37,7 @@ export class DSpaceRESTv2Serializer<T> implements Serializer<T> {
* @param models The array of models to serialize
* @returns An object to send to the backend
*/
serializeArray(models: Array<T>): any {
serializeArray(models: T[]): any {
return Serialize(models, this.modelType);
}
@@ -52,8 +53,8 @@ export class DSpaceRESTv2Serializer<T> implements Serializer<T> {
if (Array.isArray(response)) {
throw new Error('Expected a single model, use deserializeArray() instead');
}
let normalized = Object.assign({}, response, this.normalizeLinks(response._links));
return <T>Deserialize(normalized, this.modelType);
const normalized = Object.assign({}, response, this.normalizeLinks(response._links));
return Deserialize(normalized, this.modelType) as T;
}
/**
@@ -62,28 +63,27 @@ export class DSpaceRESTv2Serializer<T> implements Serializer<T> {
* @param response An object returned by the backend
* @returns an array of models of type T
*/
deserializeArray(response: any): Array<T> {
//TODO enable validation, once rest data stabilizes
deserializeArray(response: any): T[] {
// TODO: enable validation, once rest data stabilizes
// new DSpaceRESTv2Validator(response).validate();
if (!Array.isArray(response)) {
throw new Error('Expected an Array, use deserialize() instead');
}
let normalized = response.map((resource) => {
const normalized = response.map((resource) => {
return Object.assign({}, resource, this.normalizeLinks(resource._links));
});
return <Array<T>>Deserialize(normalized, this.modelType);
return Deserialize(normalized, this.modelType) as T[];
}
private normalizeLinks(links: any): any {
let normalizedLinks = links;
for (let link in normalizedLinks) {
const normalizedLinks = links;
for (const link in normalizedLinks) {
if (Array.isArray(normalizedLinks[link])) {
normalizedLinks[link] = normalizedLinks[link].map(linkedResource => {
normalizedLinks[link] = normalizedLinks[link].map((linkedResource) => {
return linkedResource.href;
});
}
else {
} else {
normalizedLinks[link] = normalizedLinks[link].href;
}
}

View File

@@ -1,16 +1,18 @@
import { Inject, Injectable } from '@angular/core';
import { Http, RequestOptionsArgs } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { RESTURLCombiner } from "../url-combiner/rest-url-combiner";
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
import { DSpaceRESTV2Response } from './dspace-rest-v2-response.model';
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
import { DSpaceRESTV2Response } from "./dspace-rest-v2-response.model";
/**
* Service to access DSpace's REST API
*/
@Injectable()
export class DSpaceRESTv2Service {
constructor(private http: Http, @Inject(GLOBAL_CONFIG) private EnvConfig: GlobalConfig) {
}
@@ -27,8 +29,8 @@ export class DSpaceRESTv2Service {
*/
get(absoluteURL: string, options?: RequestOptionsArgs): Observable<DSpaceRESTV2Response> {
return this.http.get(absoluteURL, options)
.map(res => ({ payload: res.json(), statusCode: res.statusText }))
.catch(err => {
.map((res) => ({ payload: res.json(), statusCode: res.statusText }))
.catch((err) => {
console.log('Error: ', err);
return Observable.throw(err);
});

View File

@@ -1,5 +1,6 @@
import { Validator } from 'jsonschema';
import schema from './dspace-rest-v2.schema.json'
import { Validator } from "jsonschema";
/**
* Verifies a document is a valid response from
@@ -22,11 +23,10 @@ export class DSpaceRESTv2Validator {
if (result.errors && result.errors.length > 0) {
const message = result.errors
.map((error) => error.message)
.join("\n");
.join('\n');
throw new Error(message);
}
else {
throw new Error("JSON API validation failed for an unknown reason");
} else {
throw new Error('JSON API validation failed for an unknown reason');
}
}
}

View File

@@ -5,19 +5,23 @@ import {
inject,
TestBed
} from '@angular/core/testing';
import {
CUSTOM_ELEMENTS_SCHEMA,
DebugElement
} from "@angular/core";
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { By } from '@angular/platform-browser';
import { TranslateModule, TranslateLoader } from "@ngx-translate/core";
import { Store, StoreModule } from "@ngrx/store";
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { Store, StoreModule } from '@ngrx/store';
// Load the implementations that should be tested
import { FooterComponent } from './footer.component';
import { CommonModule } from '@angular/common';
import { MockTranslateLoader } from "../../shared/testing/mock-translate-loader";
import { MockTranslateLoader } from '../../shared/testing/mock-translate-loader';
let comp: FooterComponent;
let fixture: ComponentFixture<FooterComponent>;

View File

@@ -1,18 +1,12 @@
import { Component, OnInit } from "@angular/core";
import { Component } from '@angular/core';
@Component({
selector: 'ds-footer',
styleUrls: ['footer.component.scss'],
templateUrl: 'footer.component.html'
})
export class FooterComponent implements OnInit {
export class FooterComponent {
dateObj: number = Date.now();
constructor() {
}
ngOnInit(): void {
}
}

View File

@@ -1,5 +1,6 @@
import { Action } from "@ngrx/store";
import { type } from "../../shared/ngrx/type";
import { Action } from '@ngrx/store';
import { type } from '../../shared/ngrx/type';
/**
* The list of HrefIndexAction type definitions
@@ -9,6 +10,7 @@ export const HrefIndexActionTypes = {
REMOVE_UUID: type('dspace/core/index/href/REMOVE_UUID')
};
/* tslint:disable:max-classes-per-file */
/**
* An ngrx action to add an href to the index
*/
@@ -48,11 +50,11 @@ export class RemoveUUIDFromHrefIndexAction implements Action {
constructor(uuid: string) {
this.payload = uuid;
}
}
/* tslint:enable:max-classes-per-file */
/**
* A type to encompass all HrefIndexActions
*/
export type HrefIndexAction
= AddToHrefIndexAction
| RemoveUUIDFromHrefIndexAction;
export type HrefIndexAction = AddToHrefIndexAction | RemoveUUIDFromHrefIndexAction;

View File

@@ -1,19 +1,16 @@
import { Injectable } from "@angular/core";
import { Effect, Actions } from "@ngrx/effects";
import { Injectable } from '@angular/core';
import { Effect, Actions } from '@ngrx/effects';
import {
ObjectCacheActionTypes, AddToObjectCacheAction,
RemoveFromObjectCacheAction
} from "../cache/object-cache.actions";
import { AddToHrefIndexAction, RemoveUUIDFromHrefIndexAction } from "./href-index.actions";
import { hasValue } from "../../shared/empty.util";
} from '../cache/object-cache.actions';
import { AddToHrefIndexAction, RemoveUUIDFromHrefIndexAction } from './href-index.actions';
import { hasValue } from '../../shared/empty.util';
@Injectable()
export class HrefIndexEffects {
constructor(
private actions$: Actions
) { }
@Effect() add$ = this.actions$
.ofType(ObjectCacheActionTypes.ADD)
.filter((action: AddToObjectCacheAction) => hasValue(action.payload.objectToCache.self))
@@ -29,4 +26,9 @@ export class HrefIndexEffects {
.map((action: RemoveFromObjectCacheAction) => {
return new RemoveUUIDFromHrefIndexAction(action.payload);
});
constructor(private actions$: Actions) {
}
}

View File

@@ -1,7 +1,10 @@
import {
HrefIndexAction, HrefIndexActionTypes, AddToHrefIndexAction,
HrefIndexAction,
HrefIndexActionTypes,
AddToHrefIndexAction,
RemoveUUIDFromHrefIndexAction
} from "./href-index.actions";
} from './href-index.actions';
export interface HrefIndexState {
[href: string]: string
}
@@ -13,11 +16,11 @@ export const hrefIndexReducer = (state = initialState, action: HrefIndexAction):
switch (action.type) {
case HrefIndexActionTypes.ADD: {
return addToHrefIndex(state, <AddToHrefIndexAction>action);
return addToHrefIndex(state, action as AddToHrefIndexAction);
}
case HrefIndexActionTypes.REMOVE_UUID: {
return removeUUIDFromHrefIndex(state, <RemoveUUIDFromHrefIndexAction>action)
return removeUUIDFromHrefIndex(state, action as RemoveUUIDFromHrefIndexAction)
}
default: {
@@ -33,8 +36,8 @@ function addToHrefIndex(state: HrefIndexState, action: AddToHrefIndexAction): Hr
}
function removeUUIDFromHrefIndex(state: HrefIndexState, action: RemoveUUIDFromHrefIndexAction): HrefIndexState {
let newState = Object.create(null);
for (let href in state) {
const newState = Object.create(null);
for (const href in state) {
if (state[href] !== action.payload) {
newState[href] = state[href];
}

View File

@@ -1,5 +1,6 @@
import { combineReducers } from "@ngrx/store";
import { HrefIndexState, hrefIndexReducer } from "./href-index.reducer";
import { combineReducers } from '@ngrx/store';
import { HrefIndexState, hrefIndexReducer } from './href-index.reducer';
export interface IndexState {
href: HrefIndexState

View File

@@ -18,7 +18,7 @@ export interface Serializer<T> {
* @param models The array of models to serialize
* @returns An object to send to the backend
*/
serializeArray(models: Array<T>): any;
serializeArray(models: T[]): any;
/**
* Convert a response from the backend in to a model.
@@ -34,5 +34,5 @@ export interface Serializer<T> {
* @param response An object returned by the backend
* @returns an array of models of type T
*/
deserializeArray(response: any): Array<T>;
deserializeArray(response: any): T[];
}

View File

@@ -1,6 +1,6 @@
import { DSpaceObject } from "./dspace-object.model";
import { RemoteData } from "../data/remote-data";
import { Item } from "./item.model";
import { DSpaceObject } from './dspace-object.model';
import { RemoteData } from '../data/remote-data';
import { Item } from './item.model';
export class Bitstream extends DSpaceObject {

View File

@@ -1,7 +1,7 @@
import { DSpaceObject } from "./dspace-object.model";
import { Bitstream } from "./bitstream.model";
import { Item } from "./item.model";
import { RemoteData } from "../data/remote-data";
import { DSpaceObject } from './dspace-object.model';
import { Bitstream } from './bitstream.model';
import { Item } from './item.model';
import { RemoteData } from '../data/remote-data';
export class Bundle extends DSpaceObject {
/**

View File

@@ -1,7 +1,7 @@
import { DSpaceObject } from "./dspace-object.model";
import { Bitstream } from "./bitstream.model";
import { Item } from "./item.model";
import { RemoteData } from "../data/remote-data";
import { DSpaceObject } from './dspace-object.model';
import { Bitstream } from './bitstream.model';
import { Item } from './item.model';
import { RemoteData } from '../data/remote-data';
export class Collection extends DSpaceObject {
@@ -15,7 +15,7 @@ export class Collection extends DSpaceObject {
* Corresponds to the metadata field dc.description
*/
get introductoryText(): string {
return this.findMetadata("dc.description");
return this.findMetadata('dc.description');
}
/**
@@ -23,7 +23,7 @@ export class Collection extends DSpaceObject {
* Corresponds to the metadata field dc.description.abstract
*/
get shortDescription(): string {
return this.findMetadata("dc.description.abstract");
return this.findMetadata('dc.description.abstract');
}
/**
@@ -31,7 +31,7 @@ export class Collection extends DSpaceObject {
* Corresponds to the metadata field dc.rights
*/
get copyrightText(): string {
return this.findMetadata("dc.rights");
return this.findMetadata('dc.rights');
}
/**
@@ -39,7 +39,7 @@ export class Collection extends DSpaceObject {
* Corresponds to the metadata field dc.rights.license
*/
get license(): string {
return this.findMetadata("dc.rights.license");
return this.findMetadata('dc.rights.license');
}
/**
@@ -47,7 +47,7 @@ export class Collection extends DSpaceObject {
* Corresponds to the metadata field dc.description.tableofcontents
*/
get sidebarText(): string {
return this.findMetadata("dc.description.tableofcontents");
return this.findMetadata('dc.description.tableofcontents');
}
/**

View File

@@ -1,7 +1,7 @@
import { DSpaceObject } from "./dspace-object.model";
import { Bitstream } from "./bitstream.model";
import { Collection } from "./collection.model";
import { RemoteData } from "../data/remote-data";
import { DSpaceObject } from './dspace-object.model';
import { Bitstream } from './bitstream.model';
import { Collection } from './collection.model';
import { RemoteData } from '../data/remote-data';
export class Community extends DSpaceObject {
@@ -15,7 +15,7 @@ export class Community extends DSpaceObject {
* Corresponds to the metadata field dc.description
*/
get introductoryText(): string {
return this.findMetadata("dc.description");
return this.findMetadata('dc.description');
}
/**
@@ -23,7 +23,7 @@ export class Community extends DSpaceObject {
* Corresponds to the metadata field dc.description.abstract
*/
get shortDescription(): string {
return this.findMetadata("dc.description.abstract");
return this.findMetadata('dc.description.abstract');
}
/**
@@ -31,7 +31,7 @@ export class Community extends DSpaceObject {
* Corresponds to the metadata field dc.rights
*/
get copyrightText(): string {
return this.findMetadata("dc.rights");
return this.findMetadata('dc.rights');
}
/**
@@ -39,7 +39,7 @@ export class Community extends DSpaceObject {
* Corresponds to the metadata field dc.description.tableofcontents
*/
get sidebarText(): string {
return this.findMetadata("dc.description.tableofcontents");
return this.findMetadata('dc.description.tableofcontents');
}
/**

View File

@@ -1,8 +1,8 @@
import { Metadatum } from "./metadatum.model"
import { isEmpty, isNotEmpty } from "../../shared/empty.util";
import { CacheableObject } from "../cache/object-cache.reducer";
import { RemoteData } from "../data/remote-data";
import { ResourceType } from "./resource-type";
import { Metadatum } from './metadatum.model'
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
import { CacheableObject } from '../cache/object-cache.reducer';
import { RemoteData } from '../data/remote-data';
import { ResourceType } from './resource-type';
/**
* An abstract model class for a DSpaceObject.
@@ -34,7 +34,7 @@ export abstract class DSpaceObject implements CacheableObject {
/**
* An array containing all metadata of this DSpaceObject
*/
metadata: Array<Metadatum>;
metadata: Metadatum[];
/**
* An array of DSpaceObjects that are direct parents of this DSpaceObject
@@ -58,15 +58,12 @@ export abstract class DSpaceObject implements CacheableObject {
* @return string
*/
findMetadata(key: string, language?: string): string {
const metadatum = this.metadata
.find((metadatum: Metadatum) => {
return metadatum.key === key &&
(isEmpty(language) || metadatum.language === language)
});
const metadatum = this.metadata.find((m: Metadatum) => {
return m.key === key && (isEmpty(language) || m.language === language)
});
if (isNotEmpty(metadatum)) {
return metadatum.value;
}
else {
} else {
return undefined;
}
}
@@ -81,10 +78,10 @@ export abstract class DSpaceObject implements CacheableObject {
* @param key(s)
* @return Array<Metadatum>
*/
filterMetadata(keys: string[]): Array<Metadatum> {
return this.metadata
.filter((metadatum: Metadatum) => {
return keys.some(key => key === metadatum.key);
});
filterMetadata(keys: string[]): Metadatum[] {
return this.metadata.filter((metadatum: Metadatum) => {
return keys.some((key) => key === metadatum.key);
});
}
}

View File

@@ -3,4 +3,6 @@
* more details:
* https://github.com/Microsoft/TypeScript/issues/204#issuecomment-257722306
*/
export type GenericConstructor<T> = { new (...args: any[]): T };
/* tslint:disable:interface-over-type-literal */
export type GenericConstructor<T> = { new(...args: any[]): T };
/* tslint:enable:interface-over-type-literal */

View File

@@ -1,22 +1,21 @@
import { Item } from "./item.model";
import { Observable } from "rxjs";
import { RemoteData } from "../data/remote-data";
import { Bitstream } from "./bitstream.model";
import { isEmpty } from "../../shared/empty.util";
import { PageInfo } from "./page-info.model";
import { Observable } from 'rxjs/Observable';
import { Item } from './item.model';
import { RemoteData } from '../data/remote-data';
import { Bitstream } from './bitstream.model';
import { isEmpty } from '../../shared/empty.util';
import { PageInfo } from './page-info.model';
describe('Item', () => {
let item: Item;
const thumbnailBundleName = "THUMBNAIL";
const originalBundleName = "ORIGINAL";
const thumbnailPath = "thumbnail.jpg";
const bitstream1Path = "document.pdf";
const bitstream2Path = "otherfile.doc";
const thumbnailBundleName = 'THUMBNAIL';
const originalBundleName = 'ORIGINAL';
const thumbnailPath = 'thumbnail.jpg';
const bitstream1Path = 'document.pdf';
const bitstream2Path = 'otherfile.doc';
const nonExistingBundleName = "c1e568f7-d14e-496b-bdd7-07026998cc00";
const nonExistingBundleName = 'c1e568f7-d14e-496b-bdd7-07026998cc00';
let bitstreams;
let remoteDataThumbnail;
let remoteDataFiles;
@@ -30,14 +29,13 @@ describe('Item', () => {
bitstreams = [{
retrieve: bitstream1Path
}, {
retrieve: bitstream2Path
}];
retrieve: bitstream2Path
}];
remoteDataThumbnail = createRemoteDataObject(thumbnail);
remoteDataFiles = createRemoteDataObject(bitstreams);
remoteDataAll = createRemoteDataObject([...bitstreams, thumbnail]);
// Create Bundles
const bundles =
@@ -52,51 +50,47 @@ describe('Item', () => {
bitstreams: remoteDataFiles
}];
item = Object.assign(new Item(), { bitstreams: remoteDataAll });
});
it('should return the bitstreams related to this item with the specified bundle name', () => {
const bitObs: Observable<Bitstream[]> = item.getBitstreamsByBundleName(thumbnailBundleName);
bitObs.take(1).subscribe(bs =>
expect(bs.every(b => b.name === thumbnailBundleName)).toBeTruthy());
bitObs.take(1).subscribe((bs) =>
expect(bs.every((b) => b.name === thumbnailBundleName)).toBeTruthy());
});
it('should return an empty array when no bitstreams with this bundleName exist for this item', () => {
const bitstreams: Observable<Bitstream[]> = item.getBitstreamsByBundleName(nonExistingBundleName);
bitstreams.take(1).subscribe(bs => expect(isEmpty(bs)).toBeTruthy());
const bs: Observable<Bitstream[]> = item.getBitstreamsByBundleName(nonExistingBundleName);
bs.take(1).subscribe((b) => expect(isEmpty(b)).toBeTruthy());
});
describe("get thumbnail", () => {
describe('get thumbnail', () => {
beforeEach(() => {
spyOn(item, 'getBitstreamsByBundleName').and.returnValue(Observable.of([remoteDataThumbnail]));
});
it('should return the thumbnail of this item', () => {
let path: string = thumbnailPath;
let bitstream: Observable<Bitstream> = item.getThumbnail();
bitstream.map(b => expect(b.retrieve).toBe(path));
const path: string = thumbnailPath;
const bitstream: Observable<Bitstream> = item.getThumbnail();
bitstream.map((b) => expect(b.retrieve).toBe(path));
});
});
describe("get files", () => {
describe('get files', () => {
beforeEach(() => {
spyOn(item, 'getBitstreamsByBundleName').and.returnValue(Observable.of(bitstreams));
});
it('should return all bitstreams with "ORIGINAL" as bundleName', () => {
let paths = [bitstream1Path, bitstream2Path];
it("should return all bitstreams with 'ORIGINAL' as bundleName", () => {
const paths = [bitstream1Path, bitstream2Path];
let files: Observable<Bitstream[]> = item.getFiles();
const files: Observable<Bitstream[]> = item.getFiles();
let index = 0;
files.map(f => expect(f.length).toBe(2));
files.map((f) => expect(f.length).toBe(2));
files.subscribe(
array => array.forEach(
file => {
(array) => array.forEach(
(file) => {
expect(file.retrieve).toBe(paths[index]);
index++;
}
@@ -106,16 +100,15 @@ describe('Item', () => {
});
});
function createRemoteDataObject(object: Object) {
const self = "";
function createRemoteDataObject(object: any) {
const self = '';
const requestPending = Observable.of(false);
const responsePending = Observable.of(false);
const isSuccessful = Observable.of(true);
const errorMessage = Observable.of(undefined);
const statusCode = Observable.of("200");
const statusCode = Observable.of('200');
const pageInfo = Observable.of(new PageInfo());
const payload = Observable.of(object);
return new RemoteData(

View File

@@ -1,9 +1,10 @@
import { DSpaceObject } from "./dspace-object.model";
import { Collection } from "./collection.model";
import { RemoteData } from "../data/remote-data";
import { Bitstream } from "./bitstream.model";
import { Observable } from "rxjs";
import { isNotEmpty } from "../../shared/empty.util";
import { Observable } from 'rxjs/Observable';
import { DSpaceObject } from './dspace-object.model';
import { Collection } from './collection.model';
import { RemoteData } from '../data/remote-data';
import { Bitstream } from './bitstream.model';
import { isNotEmpty } from '../../shared/empty.util';
export class Item extends DSpaceObject {
@@ -48,38 +49,36 @@ export class Item extends DSpaceObject {
bitstreams: RemoteData<Bitstream[]>;
/**
* Retrieves the thumbnail of this item
* @returns {Observable<Bitstream>} the primaryBitstream of the "THUMBNAIL" bundle
* @returns {Observable<Bitstream>} the primaryBitstream of the 'THUMBNAIL' bundle
*/
getThumbnail(): Observable<Bitstream> {
//TODO currently this just picks the first thumbnail
//should be adjusted when we have a way to determine
//the primary thumbnail from rest
return this.getBitstreamsByBundleName("THUMBNAIL")
.filter(thumbnails => isNotEmpty(thumbnails))
.map(thumbnails => thumbnails[0])
// TODO: currently this just picks the first thumbnail
// should be adjusted when we have a way to determine
// the primary thumbnail from rest
return this.getBitstreamsByBundleName('THUMBNAIL')
.filter((thumbnails) => isNotEmpty(thumbnails))
.map((thumbnails) => thumbnails[0])
}
/**
* Retrieves the thumbnail for the given original of this item
* @returns {Observable<Bitstream>} the primaryBitstream of the "THUMBNAIL" bundle
* @returns {Observable<Bitstream>} the primaryBitstream of the 'THUMBNAIL' bundle
*/
getThumbnailForOriginal(original: Bitstream): Observable<Bitstream> {
return this.getBitstreamsByBundleName("THUMBNAIL").map(files => files
.find(thumbnail => thumbnail
.name.startsWith(original.name)
)
).startWith(undefined);
return this.getBitstreamsByBundleName('THUMBNAIL')
.map((files) => {
return files.find((thumbnail) => thumbnail.name.startsWith(original.name))
}).startWith(undefined);
}
/**
* Retrieves all files that should be displayed on the item page of this item
* @returns {Observable<Array<Observable<Bitstream>>>} an array of all Bitstreams in the "ORIGINAL" bundle
* @returns {Observable<Array<Observable<Bitstream>>>} an array of all Bitstreams in the 'ORIGINAL' bundle
*/
getFiles(): Observable<Bitstream[]> {
return this.getBitstreamsByBundleName("ORIGINAL");
return this.getBitstreamsByBundleName('ORIGINAL');
}
/**
@@ -89,9 +88,9 @@ export class Item extends DSpaceObject {
*/
getBitstreamsByBundleName(bundleName: string): Observable<Bitstream[]> {
return this.bitstreams.payload.startWith([])
.map(bitstreams => bitstreams
.filter(bitstream => bitstream.bundleName === bundleName)
);
.map((bitstreams) => {
return bitstreams.filter((bitstream) => bitstream.bundleName === bundleName)
});
}
}

View File

@@ -1,4 +1,5 @@
import { autoserialize } from "cerialize";
import { autoserialize } from 'cerialize';
export class Metadatum {
/**
@@ -18,4 +19,5 @@ export class Metadatum {
*/
@autoserialize
value: string;
}

View File

@@ -1,4 +1,4 @@
import { autoserialize, autoserializeAs } from "cerialize";
import { autoserialize, autoserializeAs } from 'cerialize';
/**
* Represents the state of a paginated response
@@ -27,4 +27,5 @@ export class PageInfo {
*/
@autoserializeAs(Number, 'number')
currentPage: number;
}

View File

@@ -3,10 +3,10 @@
* https://github.com/Microsoft/TypeScript/pull/15486
*/
export enum ResourceType {
Bundle = <any>"bundle",
Bitstream = <any>"bitstream",
BitstreamFormat = <any>"bitstreamformat",
Item = <any>"item",
Collection = <any>"collection",
Community = <any>"community"
Bundle = 'bundle' as any,
Bitstream = 'bitstream' as any,
BitstreamFormat = 'bitstreamformat' as any,
Item = 'item' as any,
Collection = 'collection' as any,
Community = 'community' as any,
}

View File

@@ -1,4 +1,4 @@
import { URLCombiner } from "./url-combiner";
import { URLCombiner } from './url-combiner';
import { GlobalConfig } from '../../../config';
@@ -9,7 +9,7 @@ import { GlobalConfig } from '../../../config';
* TODO write tests once GlobalConfig becomes injectable
*/
export class RESTURLCombiner extends URLCombiner {
constructor(EnvConfig: GlobalConfig, ...parts: Array<string>) {
constructor(EnvConfig: GlobalConfig, ...parts: string[]) {
super(EnvConfig.rest.baseUrl, EnvConfig.rest.nameSpace, ...parts);
}
}

View File

@@ -1,5 +1,5 @@
import { URLCombiner } from "./url-combiner";
import { GlobalConfig } from "../../../config";
import { URLCombiner } from './url-combiner';
import { GlobalConfig } from '../../../config';
/**
* Combines a variable number of strings representing parts
@@ -8,7 +8,7 @@ import { GlobalConfig } from "../../../config";
* TODO write tests once GlobalConfig becomes injectable
*/
export class UIURLCombiner extends URLCombiner {
constructor(EnvConfig: GlobalConfig, ...parts: Array<string>) {
constructor(EnvConfig: GlobalConfig, ...parts: string[]) {
super(EnvConfig.ui.baseUrl, EnvConfig.ui.nameSpace, ...parts);
}
}

View File

@@ -1,23 +1,23 @@
import { URLCombiner } from "./url-combiner";
import { URLCombiner } from './url-combiner';
describe("URLCombiner", () => {
describe('URLCombiner', () => {
it("should return a valid URL when created with a valid set of url parts", () => {
it('should return a valid URL when created with a valid set of url parts', () => {
const url = new URLCombiner('http://foo.com', 'bar', 'id', '5').toString();
expect(url).toBe('http://foo.com/bar/id/5');
});
it("should return a URL with the protocol followed by two slashes", () => {
it('should return a URL with the protocol followed by two slashes', () => {
const url = new URLCombiner('http:/foo.com').toString();
expect(url).toBe('http://foo.com');
});
it("should return a URL with a single slash between each part", () => {
it('should return a URL with a single slash between each part', () => {
const url = new URLCombiner('http://foo.com/', '/bar/', '//id', '///5').toString();
expect(url).toBe('http://foo.com/bar/id/5');
});
it("should return a URL without a trailing slash before its parameters", () => {
it('should return a URL without a trailing slash before its parameters', () => {
const url1 = new URLCombiner('http://foo.com/', '?bar=25').toString();
const url2 = new URLCombiner('http://foo.com/', '#bar').toString();
@@ -25,7 +25,7 @@ describe("URLCombiner", () => {
expect(url2).toBe('http://foo.com#bar');
});
it("should return an empty string when created without url parts", () => {
it('should return an empty string when created without url parts', () => {
const url = new URLCombiner().toString();
expect(url).toBe('');
});

View File

@@ -1,11 +1,11 @@
import { isEmpty } from "../../shared/empty.util";
import { isEmpty } from '../../shared/empty.util';
/**
* Combines a variable number of strings representing parts
* of a URL in to a single, normalized URL
*/
export class URLCombiner {
private parts: Array<string>;
private parts: string[];
/**
* Creates a new URLCombiner
@@ -13,7 +13,7 @@ export class URLCombiner {
* @param parts
* a variable number of strings representing parts of a URL
*/
constructor(...parts: Array<string>) {
constructor(...parts: string[]) {
// can't do this in the constructor signature,
// because of the spread operator
this.parts = parts;
@@ -32,8 +32,7 @@ export class URLCombiner {
toString(): string {
if (isEmpty(this.parts)) {
return '';
}
else {
} else {
let url = this.parts.join('/');
// make sure protocol is followed by two slashes

View File

@@ -1,37 +1,34 @@
import { Action } from "@ngrx/store";
import { type } from "../shared/ngrx/type";
import { Action } from '@ngrx/store';
import { type } from '../shared/ngrx/type';
/**
* For each action type in an action group, make a simple
* enum object for all of this group's action types.
*
* The 'type' utility function coerces strings into string
* literal types and runs a simple check to guarantee all
* action types in the application are unique.
*/
* For each action type in an action group, make a simple
* enum object for all of this group's action types.
*
* The 'type' utility function coerces strings into string
* literal types and runs a simple check to guarantee all
* action types in the application are unique.
*/
export const HeaderActionTypes = {
COLLAPSE: type('dspace/header/COLLAPSE'),
EXPAND: type('dspace/header/EXPAND'),
TOGGLE: type('dspace/header/TOGGLE')
};
/* tslint:disable:max-classes-per-file */
export class HeaderCollapseAction implements Action {
type = HeaderActionTypes.COLLAPSE;
constructor() { }
}
export class HeaderExpandAction implements Action {
type = HeaderActionTypes.EXPAND;
constructor() { }
}
export class HeaderToggleAction implements Action {
type = HeaderActionTypes.TOGGLE;
constructor() { }
}
/* tslint:enable:max-classes-per-file */
/**
* Export a type alias of all actions in this action group

View File

@@ -1,13 +1,16 @@
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { HeaderComponent } from "./header.component";
import { Store, StoreModule } from "@ngrx/store";
import { HeaderState } from "./header.reducer";
import { Store, StoreModule } from '@ngrx/store';
import { TranslateModule } from '@ngx-translate/core';
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
import { Observable } from 'rxjs/Observable';
import { HeaderComponent } from './header.component';
import { HeaderState } from './header.reducer';
import { HeaderToggleAction } from './header.actions';
import Spy = jasmine.Spy;
import { HeaderToggleAction } from "./header.actions";
import { TranslateModule } from "@ngx-translate/core";
import { NgbCollapseModule } from "@ng-bootstrap/ng-bootstrap";
import { Observable } from "rxjs";
let comp: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
@@ -30,7 +33,6 @@ describe('HeaderComponent', () => {
comp = fixture.componentInstance;
store = fixture.debugElement.injector.get(Store);
spyOn(store, 'dispatch');
});
@@ -38,17 +40,17 @@ describe('HeaderComponent', () => {
describe('when the toggle button is clicked', () => {
beforeEach(() => {
let navbarToggler = fixture.debugElement.query(By.css('.navbar-toggler'));
const navbarToggler = fixture.debugElement.query(By.css('.navbar-toggler'));
navbarToggler.triggerEventHandler('click', null);
});
it("should dispatch a HeaderToggleAction", () => {
it('should dispatch a HeaderToggleAction', () => {
expect(store.dispatch).toHaveBeenCalledWith(new HeaderToggleAction());
});
});
describe("when navCollapsed in the store is true", () => {
describe('when navCollapsed in the store is true', () => {
let menu: HTMLElement;
beforeEach(() => {
@@ -57,13 +59,13 @@ describe('HeaderComponent', () => {
fixture.detectChanges();
});
it("should close the menu", () => {
it('should close the menu', () => {
expect(menu.classList).not.toContain('show');
});
});
describe("when navCollapsed in the store is false", () => {
describe('when navCollapsed in the store is false', () => {
let menu: HTMLElement;
beforeEach(() => {
@@ -72,7 +74,7 @@ describe('HeaderComponent', () => {
fixture.detectChanges();
});
it("should open the menu", () => {
it('should open the menu', () => {
expect(menu.classList).toContain('show');
});

View File

@@ -1,8 +1,9 @@
import { Component, OnInit } from "@angular/core";
import { Store } from "@ngrx/store";
import { HeaderState } from "./header.reducer";
import { Observable } from "rxjs";
import { HeaderToggleAction } from "./header.actions";
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { HeaderState } from './header.reducer';
import { HeaderToggleAction } from './header.actions';
@Component({
selector: 'ds-header',
@@ -19,7 +20,7 @@ export class HeaderComponent implements OnInit {
ngOnInit(): void {
this.isNavBarCollapsed = this.store.select('header')
//unwrap navCollapsed
// unwrap navCollapsed
.map(({ navCollapsed }: HeaderState) => navCollapsed);
}

View File

@@ -1,9 +1,10 @@
import { TestBed, inject } from "@angular/core/testing";
import { TestBed, inject } from '@angular/core/testing';
import { EffectsTestingModule, EffectsRunner } from '@ngrx/effects/testing';
import { HeaderEffects } from "./header.effects";
import { HeaderCollapseAction } from "./header.actions";
import { HostWindowResizeAction } from "../shared/host-window.actions";
import { routerActions } from "@ngrx/router-store";
import { routerActions } from '@ngrx/router-store';
import { HeaderEffects } from './header.effects';
import { HeaderCollapseAction } from './header.actions';
import { HostWindowResizeAction } from '../shared/host-window.actions';
describe('HeaderEffects', () => {
beforeEach(() => TestBed.configureTestingModule({
@@ -32,7 +33,7 @@ describe('HeaderEffects', () => {
it('should return a COLLAPSE action in response to a RESIZE action', () => {
runner.queue(new HostWindowResizeAction(800, 600));
headerEffects.resize$.subscribe(result => {
headerEffects.resize$.subscribe((result) => {
expect(result).toEqual(new HeaderCollapseAction());
});
});
@@ -44,7 +45,7 @@ describe('HeaderEffects', () => {
it('should return a COLLAPSE action in response to an UPDATE_LOCATION action', () => {
runner.queue({ type: routerActions.UPDATE_LOCATION });
headerEffects.resize$.subscribe(result => {
headerEffects.resize$.subscribe((result) => {
expect(result).toEqual(new HeaderCollapseAction());
});
});

View File

@@ -1,16 +1,13 @@
import { Injectable } from "@angular/core";
import { Injectable } from '@angular/core';
import { Effect, Actions } from '@ngrx/effects'
import { HostWindowActionTypes } from "../shared/host-window.actions";
import { routerActions } from "@ngrx/router-store";
import { HeaderCollapseAction } from "./header.actions";
import { routerActions } from '@ngrx/router-store';
import { HostWindowActionTypes } from '../shared/host-window.actions';
import { HeaderCollapseAction } from './header.actions';
@Injectable()
export class HeaderEffects {
constructor(
private actions$: Actions
) { }
@Effect() resize$ = this.actions$
.ofType(HostWindowActionTypes.RESIZE)
.map(() => new HeaderCollapseAction());
@@ -18,4 +15,9 @@ export class HeaderEffects {
@Effect() routeChange$ = this.actions$
.ofType(routerActions.UPDATE_LOCATION)
.map(() => new HeaderCollapseAction());
constructor(private actions$: Actions) {
}
}

View File

@@ -1,11 +1,11 @@
import * as deepFreeze from "deep-freeze";
import * as deepFreeze from 'deep-freeze';
import { headerReducer } from "./header.reducer";
import { headerReducer } from './header.reducer';
import {
HeaderCollapseAction,
HeaderExpandAction,
HeaderToggleAction
} from "./header.actions";
} from './header.actions';
class NullAction extends HeaderCollapseAction {
type = null;
@@ -15,8 +15,8 @@ class NullAction extends HeaderCollapseAction {
}
}
describe("headerReducer", () => {
it("should return the current state when no valid actions have been made", () => {
describe('headerReducer', () => {
it('should return the current state when no valid actions have been made', () => {
const state = { navCollapsed: false };
const action = new NullAction();
const newState = headerReducer(state, action);
@@ -24,7 +24,7 @@ describe("headerReducer", () => {
expect(newState).toEqual(state);
});
it("should start with navCollapsed = true", () => {
it('should start with navCollapsed = true', () => {
const action = new NullAction();
const initialState = headerReducer(undefined, action);
@@ -32,7 +32,7 @@ describe("headerReducer", () => {
expect(initialState.navCollapsed).toEqual(true);
});
it("should set navCollapsed to true in response to the COLLAPSE action", () => {
it('should set navCollapsed to true in response to the COLLAPSE action', () => {
const state = { navCollapsed: false };
const action = new HeaderCollapseAction();
const newState = headerReducer(state, action);
@@ -40,18 +40,18 @@ describe("headerReducer", () => {
expect(newState.navCollapsed).toEqual(true);
});
it("should perform the COLLAPSE action without affecting the previous state", () => {
it('should perform the COLLAPSE action without affecting the previous state', () => {
const state = { navCollapsed: false };
deepFreeze(state);
const action = new HeaderCollapseAction();
headerReducer(state, action);
//no expect required, deepFreeze will ensure an exception is thrown if the state
//is mutated, and any uncaught exception will cause the test to fail
// no expect required, deepFreeze will ensure an exception is thrown if the state
// is mutated, and any uncaught exception will cause the test to fail
});
it("should set navCollapsed to false in response to the EXPAND action", () => {
it('should set navCollapsed to false in response to the EXPAND action', () => {
const state = { navCollapsed: true };
const action = new HeaderExpandAction();
const newState = headerReducer(state, action);
@@ -59,7 +59,7 @@ describe("headerReducer", () => {
expect(newState.navCollapsed).toEqual(false);
});
it("should perform the EXPAND action without affecting the previous state", () => {
it('should perform the EXPAND action without affecting the previous state', () => {
const state = { navCollapsed: true };
deepFreeze(state);
@@ -67,7 +67,7 @@ describe("headerReducer", () => {
headerReducer(state, action);
});
it("should flip the value of navCollapsed in response to the TOGGLE action", () => {
it('should flip the value of navCollapsed in response to the TOGGLE action', () => {
const state1 = { navCollapsed: true };
const action = new HeaderToggleAction();
@@ -78,7 +78,7 @@ describe("headerReducer", () => {
expect(state3.navCollapsed).toEqual(true);
});
it("should perform the TOGGLE action without affecting the previous state", () => {
it('should perform the TOGGLE action without affecting the previous state', () => {
const state = { navCollapsed: true };
deepFreeze(state);

View File

@@ -1,4 +1,4 @@
import { HeaderAction, HeaderActionTypes } from "./header.actions";
import { HeaderAction, HeaderActionTypes } from './header.actions';
export interface HeaderState {
navCollapsed: boolean;

View File

@@ -1,19 +1,10 @@
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
@Component({
selector: 'ds-home-news',
styleUrls: ['./home-news.component.scss'],
templateUrl: './home-news.component.html'
})
export class HomeNewsComponent implements OnInit {
constructor() {
this.universalInit();
}
export class HomeNewsComponent {
universalInit() {
}
ngOnInit(): void {
}
}

View File

@@ -1,19 +1,10 @@
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
@Component({
selector: 'ds-home',
styleUrls: ['./home.component.scss'],
templateUrl: './home.component.html'
})
export class HomeComponent implements OnInit {
constructor() {
this.universalInit();
}
export class HomeComponent {
universalInit() {
}
ngOnInit(): void {
}
}

View File

@@ -2,12 +2,12 @@ import { NgModule } from '@angular/core';
import { HomeComponent } from './home.component';
import { HomeRoutingModule } from './home-routing.module';
import { CommonModule } from "@angular/common";
import { TopLevelCommunityListComponent } from "./top-level-community-list/top-level-community-list.component";
import { HomeNewsComponent } from "./home-news/home-news.component";
import { RouterModule } from "@angular/router";
import { TranslateModule } from "@ngx-translate/core";
import { SharedModule } from "../shared/shared.module";
import { CommonModule } from '@angular/common';
import { TopLevelCommunityListComponent } from './top-level-community-list/top-level-community-list.component';
import { HomeNewsComponent } from './home-news/home-news.component';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { SharedModule } from '../shared/shared.module';
@NgModule({
imports: [

View File

@@ -1,9 +1,10 @@
import { Component, OnInit, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';
import { RemoteData } from "../../core/data/remote-data";
import { CommunityDataService } from "../../core/data/community-data.service";
import { Community } from "../../core/shared/community.model";
import { PaginationComponentOptions } from "../../shared/pagination/pagination-component-options.model";
import { SortOptions, SortDirection } from "../../core/cache/models/sort-options.model";
import { RemoteData } from '../../core/data/remote-data';
import { CommunityDataService } from '../../core/data/community-data.service';
import { Community } from '../../core/shared/community.model';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { SortOptions, SortDirection } from '../../core/cache/models/sort-options.model';
@Component({
selector: 'ds-top-level-community-list',
@@ -20,16 +21,12 @@ export class TopLevelCommunityListComponent implements OnInit {
private cds: CommunityDataService,
private ref: ChangeDetectorRef
) {
this.universalInit();
}
universalInit() {
}
ngOnInit(): void {
this.config = new PaginationComponentOptions();
this.config.id = "top-level-pagination";
this.config.id = 'top-level-pagination';
this.config.pageSizeOptions = [4];
this.config.pageSize = 4;
this.sortConfig = new SortOptions();

View File

@@ -1,8 +1,9 @@
import { Component, Input, OnInit } from '@angular/core';
import { Collection } from "../../../core/shared/collection.model";
import { Observable } from "rxjs";
import { Item } from "../../../core/shared/item.model";
import { RemoteDataBuildService } from "../../../core/cache/builders/remote-data-build.service";
import { Observable } from 'rxjs/Observable';
import { Collection } from '../../../core/shared/collection.model';
import { Item } from '../../../core/shared/item.model';
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
/**
* This component renders the parent collections section of the item
@@ -17,30 +18,23 @@ export class CollectionsComponent implements OnInit {
@Input() item: Item;
label: string = "item.page.collections";
label = 'item.page.collections';
separator: string = "<br/>";
separator = '<br/>';
collections: Observable<Collection[]>;
constructor(
private rdbs: RemoteDataBuildService
) {
this.universalInit();
constructor(private rdbs: RemoteDataBuildService) {
}
universalInit() {
}
ngOnInit(): void {
// this.collections = this.item.parents.payload;
//TODO this should use parents, but the collections
// TODO: this should use parents, but the collections
// for an Item aren't returned by the REST API yet,
// only the owning collection
this.collections = this.item.owner.payload.map(c => [c]);
this.collections = this.item.owner.payload.map((c) => [c]);
}
}

View File

@@ -4,7 +4,6 @@ import { Component, Input } from '@angular/core';
* This component renders any content inside this wrapper.
* The wrapper prints a label before the content (if available)
*/
@Component({
selector: 'ds-metadata-field-wrapper',
styleUrls: ['./metadata-field-wrapper.component.scss'],
@@ -14,13 +13,4 @@ export class MetadataFieldWrapperComponent {
@Input() label: string;
constructor() {
this.universalInit();
}
universalInit() {
}
}

View File

@@ -1,5 +1,6 @@
import { Component, Input } from '@angular/core';
import { MetadataValuesComponent } from "../metadata-values/metadata-values.component";
import { MetadataValuesComponent } from '../metadata-values/metadata-values.component';
/**
* This component renders the configured 'values' into the ds-metadata-field-wrapper component as a link.
@@ -8,7 +9,6 @@ import { MetadataValuesComponent } from "../metadata-values/metadata-values.comp
* using the 'linktext' as it's value (if it exists)
* and using the values as the 'href' attribute (and as value of the tag when no 'linktext' is defined)
*/
@Component({
selector: 'ds-metadata-uri-values',
styleUrls: ['./metadata-uri-values.component.scss'],

View File

@@ -4,7 +4,6 @@ import { Component, Input } from '@angular/core';
* This component renders the configured 'values' into the ds-metadata-field-wrapper component.
* It puts the given 'separator' between each two values.
*/
@Component({
selector: 'ds-metadata-values',
styleUrls: ['./metadata-values.component.scss'],
@@ -18,13 +17,4 @@ export class MetadataValuesComponent {
@Input() label: string;
constructor() {
this.universalInit();
}
universalInit() {
}
}

View File

@@ -1,9 +1,10 @@
import { Component, Input, OnInit } from '@angular/core';
import { Bitstream } from "../../../../core/shared/bitstream.model";
import { Item } from "../../../../core/shared/item.model";
import { Observable } from "rxjs";
import { FileSectionComponent } from "../../../simple/field-components/file-section/file-section.component";
import { hasValue } from "../../../../shared/empty.util";
import { Observable } from 'rxjs/Observable';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { Item } from '../../../../core/shared/item.model';
import { FileSectionComponent } from '../../../simple/field-components/file-section/file-section.component';
import { hasValue } from '../../../../shared/empty.util';
/**
* This component renders the file section of the item
@@ -23,25 +24,20 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
files: Observable<Bitstream[]>;
thumbnails: Map<string, Observable<Bitstream>> = new Map();
universalInit() {
}
ngOnInit(): void {
super.ngOnInit();
}
initialize(): void {
const originals = this.item.getFiles();
const licenses = this.item.getBitstreamsByBundleName("LICENSE");
this.files = Observable.combineLatest(originals, licenses, (originals, licenses) => [...originals, ...licenses]);
const licenses = this.item.getBitstreamsByBundleName('LICENSE');
this.files = Observable.combineLatest(originals, licenses, (o, l) => [...o, ...l]);
this.files.subscribe(
files =>
(files) =>
files.forEach(
original => {
(original) => {
const thumbnail: Observable<Bitstream> = this.item.getThumbnailForOriginal(original);
this.thumbnails.set(original.id, thumbnail);
}

View File

@@ -1,11 +1,12 @@
import { Component, OnInit } from '@angular/core';
import { Observable } from "rxjs";
import { ItemPageComponent } from "../simple/item-page.component";
import { Metadatum } from "../../core/shared/metadatum.model";
import { ItemDataService } from "../../core/data/item-data.service";
import { ActivatedRoute } from "@angular/router";
import { RemoteData } from "../../core/data/remote-data";
import { Item } from "../../core/shared/item.model";
import { Observable } from 'rxjs/Observable';
import { ItemPageComponent } from '../simple/item-page.component';
import { Metadatum } from '../../core/shared/metadatum.model';
import { ItemDataService } from '../../core/data/item-data.service';
import { ActivatedRoute } from '@angular/router';
import { RemoteData } from '../../core/data/remote-data';
import { Item } from '../../core/shared/item.model';
/**
* This component renders a simple item page.
@@ -22,16 +23,12 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit {
item: RemoteData<Item>;
metadata: Observable<Array<Metadatum>>;
metadata: Observable<Metadatum[]>;
constructor(route: ActivatedRoute, items: ItemDataService) {
super(route, items);
}
universalInit() {
}
/*** AoT inheritance fix, will hopefully be resolved in the near future **/
ngOnInit(): void {
super.ngOnInit();
@@ -39,7 +36,7 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit {
initialize(params) {
super.initialize(params);
this.metadata = this.item.payload.map(i => i.metadata);
this.metadata = this.item.payload.map((i) => i.metadata);
}
}

View File

@@ -1,5 +1,6 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ItemPageComponent } from './simple/item-page.component';
import { ItemPageRoutingModule } from './item-page-routing.module';
import { MetadataValuesComponent } from './field-components/metadata-values/metadata-values.component';
@@ -12,10 +13,10 @@ import { ItemPageUriFieldComponent } from './simple/field-components/specific-fi
import { ItemPageTitleFieldComponent } from './simple/field-components/specific-field/title/item-page-title-field.component';
import { ItemPageSpecificFieldComponent } from './simple/field-components/specific-field/item-page-specific-field.component';
import { SharedModule } from './../shared/shared.module';
import { FileSectionComponent } from "./simple/field-components/file-section/file-section.component";
import { CollectionsComponent } from "./field-components/collections/collections.component";
import { FullItemPageComponent } from "./full/full-item-page.component";
import { FullFileSectionComponent } from "./full/field-components/file-section/full-file-section.component";
import { FileSectionComponent } from './simple/field-components/file-section/file-section.component';
import { CollectionsComponent } from './field-components/collections/collections.component';
import { FullItemPageComponent } from './full/full-item-page.component';
import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component';
@NgModule({
declarations: [

View File

@@ -1,13 +1,13 @@
import { Component, Input, OnInit } from '@angular/core';
import { Bitstream } from "../../../../core/shared/bitstream.model";
import { Item } from "../../../../core/shared/item.model";
import { Observable } from "rxjs";
import { Observable } from 'rxjs/Observable';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { Item } from '../../../../core/shared/item.model';
/**
* This component renders the file section of the item
* inside a 'ds-metadata-field-wrapper' component.
*/
@Component({
selector: 'ds-item-page-file-section',
templateUrl: './file-section.component.html'
@@ -16,19 +16,12 @@ export class FileSectionComponent implements OnInit {
@Input() item: Item;
label : string = "item.page.files";
label = 'item.page.files';
separator: string = "<br/>";
separator = '<br/>';
files: Observable<Bitstream[]>;
constructor() {
this.universalInit();
}
universalInit() {
}
ngOnInit(): void {
this.initialize();
}

View File

@@ -1,6 +1,7 @@
import { Component, Input } from '@angular/core';
import { Item } from "../../../../../core/shared/item.model";
import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component";
import { Item } from '../../../../../core/shared/item.model';
import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
@Component({
selector: 'ds-item-page-abstract-field',
@@ -10,12 +11,12 @@ export class ItemPageAbstractFieldComponent extends ItemPageSpecificFieldCompone
@Input() item: Item;
separator : string;
separator: string;
fields : string[] = [
"dc.description.abstract"
fields: string[] = [
'dc.description.abstract'
];
label : string = "item.page.abstract";
label = 'item.page.abstract';
}

View File

@@ -1,6 +1,7 @@
import { Component, Input } from '@angular/core';
import { Item } from "../../../../../core/shared/item.model";
import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component";
import { Item } from '../../../../../core/shared/item.model';
import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
@Component({
selector: 'ds-item-page-author-field',
@@ -10,14 +11,14 @@ export class ItemPageAuthorFieldComponent extends ItemPageSpecificFieldComponent
@Input() item: Item;
separator : string;
separator: string;
fields : string[] = [
"dc.contributor.author",
"dc.creator",
"dc.contributor"
fields: string[] = [
'dc.contributor.author',
'dc.creator',
'dc.contributor'
];
label : string = "item.page.author";
label = 'item.page.author';
}

View File

@@ -1,6 +1,7 @@
import { Component, Input } from '@angular/core';
import { Item } from "../../../../../core/shared/item.model";
import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component";
import { Item } from '../../../../../core/shared/item.model';
import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
@Component({
selector: 'ds-item-page-date-field',
@@ -10,12 +11,12 @@ export class ItemPageDateFieldComponent extends ItemPageSpecificFieldComponent {
@Input() item: Item;
separator : string = ", ";
separator = ', ';
fields : string[] = [
"dc.date.issued"
fields: string[] = [
'dc.date.issued'
];
label : string = "item.page.date";
label = 'item.page.date';
}

View File

@@ -1,5 +1,6 @@
import { Component, Input } from '@angular/core';
import { Item } from "../../../../core/shared/item.model";
import { Item } from '../../../../core/shared/item.model';
/**
* This component can be used to represent metadata on a simple item page.
@@ -17,24 +18,17 @@ export class ItemPageSpecificFieldComponent {
/**
* Fields (schema.element.qualifier) used to render their values.
*/
fields : string[];
fields: string[];
/**
* Label i18n key for the rendered metadata
*/
label : string;
label: string;
/**
* Separator string between multiple values of the metadata fields defined
* @type {string}
*/
separator : string = "<br/>";
separator = '<br/>';
constructor() {
this.universalInit();
}
universalInit() {
}
}

Some files were not shown because too many files have changed in this diff Show More